/*
- * Copyright (C) 1995-2005, Index Data ApS
+ * Copyright (C) 1995-2006, Index Data ApS
* See the file LICENSE for details.
*
- * $Id: tcpip.c,v 1.18 2006-06-09 12:40:53 adam Exp $
+ * $Id: tcpip.c,v 1.32 2006-10-13 11:22:26 adam Exp $
*/
/**
* \file tcpip.c
#endif
#ifdef WIN32
-#include <winsock.h>
+
+/* VS 2003 or later has getaddrinfo; older versions do not */
+#include <winsock2.h>
+#if _MSC_VER >= 1300
+#include <ws2tcpip.h>
+#define HAVE_GETADDRINFO 1
+#else
+#define HAVE_GETADDRINFO 0
+#endif
+
#else
#include <netinet/in.h>
#include <netdb.h>
int written; /* -1 if we aren't writing */
int towrite; /* to verify against user input */
int (*complete)(const unsigned char *buf, int len); /* length/comple. */
+#if HAVE_GETADDRINFO
+ struct addrinfo *ai;
+#else
struct sockaddr_in addr; /* returned by cs_straddr */
+#endif
char buf[128]; /* returned by cs_addrstr */
#if HAVE_OPENSSL_SSL_H
SSL_CTX *ctx; /* current CTX. */
* This function is always called through the cs_create() macro.
* s >= 0: socket has already been established for us.
*/
-COMSTACK tcpip_type(int s, int blocking, int protocol, void *vp)
+COMSTACK tcpip_type(int s, int flags, int protocol, void *vp)
{
COMSTACK p;
tcpip_state *sp;
- int new_socket;
-#ifdef WIN32
- unsigned long tru = 1;
-#endif
if (!tcpip_init ())
return 0;
- if (s < 0)
- {
- if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- return 0;
- new_socket = 1;
- }
- else
- new_socket = 0;
if (!(p = (struct comstack *)xmalloc(sizeof(struct comstack))))
return 0;
if (!(sp = (struct tcpip_state *)(p->cprivate =
xmalloc(sizeof(tcpip_state)))))
return 0;
- if (!((p->blocking = blocking)&1))
- {
-#ifdef WIN32
- if (ioctlsocket(s, FIONBIO, &tru) < 0)
- return 0;
-#else
- if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
- return 0;
-#ifndef MSG_NOSIGNAL
- signal (SIGPIPE, SIG_IGN);
-#endif
-#endif
- }
+ p->flags = flags;
p->io_pending = 0;
p->iofile = s;
p->f_addrstr = tcpip_addrstr;
p->f_straddr = tcpip_straddr;
p->f_set_blocking = tcpip_set_blocking;
+ p->max_recv_bytes = 5000000;
- p->state = new_socket ? CS_ST_UNBND : CS_ST_IDLE; /* state of line */
+ p->state = s < 0 ? CS_ST_UNBND : CS_ST_IDLE; /* state of line */
p->event = CS_NONE;
p->cerrno = 0;
p->stackerr = 0;
strcpy(sp->cert_fname, "yaz.pem");
#endif
+#if HAVE_GETADDRINFO
+ sp->ai = 0;
+#endif
sp->altbuf = 0;
sp->altsize = sp->altlen = 0;
sp->towrite = sp->written = -1;
#if HAVE_OPENSSL_SSL_H
-COMSTACK ssl_type(int s, int blocking, int protocol, void *vp)
+COMSTACK ssl_type(int s, int flags, int protocol, void *vp)
{
tcpip_state *sp;
COMSTACK p;
- p = tcpip_type (s, blocking, protocol, 0);
+ p = tcpip_type (s, flags, protocol, 0);
if (!p)
return 0;
p->f_get = ssl_get;
}
#endif
+#if HAVE_GETADDRINFO
+/* resolve using getaddrinfo */
+struct addrinfo *tcpip_getaddrinfo(const char *str, const char *port)
+{
+ struct addrinfo hints, *res;
+ int error;
+ char host[512], *p;
+
+ hints.ai_flags = 0;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ hints.ai_addrlen = 0;
+ hints.ai_addr = NULL;
+ hints.ai_canonname = NULL;
+ hints.ai_next = NULL;
+
+ strncpy(host, str, sizeof(host)-1);
+ host[sizeof(host)-1] = 0;
+ if ((p = strchr(host, '/')))
+ *p = 0;
+ if ((p = strrchr(host, ':')))
+ {
+ *p = '\0';
+ port = p+1;
+ }
+
+ if (!strcmp("@", host))
+ {
+ hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(0, port, &hints, &res);
+ }
+ else
+ {
+ error = getaddrinfo(host, port, &hints, &res);
+ }
+ if (error)
+ return 0;
+ return res;
+}
+
+#endif
+/* gethostbyname .. old systems */
int tcpip_strtoaddr_ex(const char *str, struct sockaddr_in *add,
int default_port)
{
struct hostent *hp;
char *p, buf[512];
short int port = default_port;
- unsigned tmpadd;
-
- if (!tcpip_init ())
- return 0;
+#ifdef WIN32
+ unsigned long tmpadd;
+#else
+ in_addr_t tmpadd;
+#endif
TRC(fprintf(stderr, "tcpip_strtoaddress: %s\n", str ? str : "NULL"));
add->sin_family = AF_INET;
- strncpy(buf, str, 511);
- buf[511] = 0;
+ strncpy(buf, str, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = 0;
if ((p = strchr(buf, '/')))
*p = 0;
- if ((p = strchr(buf, ':')))
+ if ((p = strrchr(buf, ':')))
{
*p = 0;
port = atoi(p + 1);
}
add->sin_port = htons(port);
if (!strcmp("@", buf))
+ {
add->sin_addr.s_addr = INADDR_ANY;
+ }
+ else if ((tmpadd = inet_addr(buf)) != -1)
+ {
+ memcpy(&add->sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
+ }
else if ((hp = gethostbyname(buf)))
+ {
memcpy(&add->sin_addr.s_addr, *hp->h_addr_list,
sizeof(struct in_addr));
- else if ((tmpadd = (unsigned) inet_addr(buf)) != 0)
- memcpy(&add->sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
+ }
else
return 0;
return 1;
}
+
+#if HAVE_GETADDRINFO
void *tcpip_straddr(COMSTACK h, const char *str)
{
tcpip_state *sp = (tcpip_state *)h->cprivate;
- int port = 210;
+ const char *port = "210";
+ if (h->protocol == PROTO_HTTP)
+ port = "80";
+ if (!tcpip_init ())
+ return 0;
+ if (sp->ai)
+ freeaddrinfo(sp->ai);
+ sp->ai = tcpip_getaddrinfo(str, port);
+ if (sp->ai && h->state == CS_ST_UNBND)
+ {
+ int s = -1;
+ struct addrinfo *ai = sp->ai;
+ for (; ai; ai = ai->ai_next)
+ {
+ s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (s != -1)
+ break;
+ }
+ if (s == -1)
+ return 0;
+ h->iofile = s;
+
+ if (!tcpip_set_blocking(h, h->flags))
+ return 0;
+ }
+ return sp->ai;
+}
+#else
+void *tcpip_straddr(COMSTACK h, const char *str)
+{
+ tcpip_state *sp = (tcpip_state *)h->cprivate;
+ int port = 210;
if (h->protocol == PROTO_HTTP)
port = 80;
+ if (!tcpip_init ())
+ return 0;
if (!tcpip_strtoaddr_ex (str, &sp->addr, port))
return 0;
- return &sp->addr;
-}
+ if (h->state == CS_ST_UNBND)
+ {
+ int s;
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ return 0;
+ h->iofile = s;
-struct sockaddr_in *tcpip_strtoaddr(const char *str)
-{
- static struct sockaddr_in add;
-
- if (!tcpip_strtoaddr_ex (str, &add, 210))
- return 0;
- return &add;
+ if (!tcpip_set_blocking(h, h->flags))
+ return 0;
+ }
+ return &sp->addr;
}
+#endif
int tcpip_more(COMSTACK h)
{
*/
int tcpip_connect(COMSTACK h, void *address)
{
- struct sockaddr_in *add = (struct sockaddr_in *)address;
+#if HAVE_GETADDRINFO
+ tcpip_state *sp = (tcpip_state *)h->cprivate;
+#else
+ struct sockaddr_in *add = (struct sockaddr_in *) address;
+#endif
int r;
#ifdef __sun__
int recbuflen;
h->cerrno = CSOUTSTATE;
return -1;
}
+#if HAVE_GETADDRINFO
+ if (sp->ai != (struct addrinfo *) address)
+ {
+ h->cerrno = CSOUTSTATE;
+ return -1;
+ }
+#endif
#ifdef __sun__
/* On Suns, you must set a bigger Receive Buffer BEFORE a call to connect
* This gives the connect a chance to negotiate with the other side
TRC(fprintf( stderr, "New Size of TCP Receive Buffer = %d\n",
recbuflen ));
#endif
+
+#if HAVE_GETADDRINFO
+ r = connect(h->iofile, sp->ai->ai_addr, sp->ai->ai_addrlen);
+ freeaddrinfo(sp->ai);
+ sp->ai = 0;
+#else
r = connect(h->iofile, (struct sockaddr *) add, sizeof(*add));
+#endif
if (r < 0)
{
#ifdef WIN32
static int tcpip_bind(COMSTACK h, void *address, int mode)
{
+ int r;
+ tcpip_state *sp = (tcpip_state *)h->cprivate;
+#if HAVE_GETADDRINFO
+#else
struct sockaddr *addr = (struct sockaddr *)address;
+#endif
#ifdef WIN32
BOOL one = 1;
#else
- unsigned long one = 1;
+ int one = 1;
+#endif
+
+#if HAVE_GETADDRINFO
+ if (sp->ai != (struct addrinfo *) address)
+ {
+ h->cerrno = CSOUTSTATE;
+ return -1;
+ }
#endif
#if HAVE_OPENSSL_SSL_H
- tcpip_state *sp = (tcpip_state *)h->cprivate;
if (h->type == ssl_type && !sp->ctx)
{
SSL_load_error_strings();
}
#endif
tcpip_setsockopt(h->iofile);
- if (bind(h->iofile, addr, sizeof(struct sockaddr_in)))
+#if HAVE_GETADDRINFO
+ r = bind(h->iofile, sp->ai->ai_addr, sp->ai->ai_addrlen);
+ freeaddrinfo(sp->ai);
+ sp->ai = 0;
+#else
+ r = bind(h->iofile, addr, sizeof(struct sockaddr_in));
+#endif
+ if (r)
{
h->cerrno = CSYSERR;
return -1;
}
return 0;
}
- if (!(cnew->blocking&1) &&
-#ifdef WIN32
- (ioctlsocket(cnew->iofile, FIONBIO, &tru) < 0)
-#else
- (fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0)
-#endif
- )
+ if (!tcpip_set_blocking(cnew, cnew->flags))
{
h->cerrno = CSYSERR;
if (h->newfd != -1)
state->altsize = state->altlen = 0;
state->towrite = state->written = -1;
state->complete = st->complete;
+#if HAVE_GETADDRINFO
+ state->ai = 0;
+#endif
cnew->state = CS_ST_ACCEPT;
h->state = CS_ST_IDLE;
if (!*bufsize)
{
if (!(*buf = (char *)xmalloc(*bufsize = CS_TCPIP_BUFCHUNK)))
+ {
+ h->cerrno = CSYSERR;
return -1;
+ }
}
else if (*bufsize - hasread < CS_TCPIP_BUFCHUNK)
if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
+ {
+ h->cerrno = CSYSERR;
return -1;
+ }
#ifdef __sun__
yaz_set_errno( 0 );
/* unfortunatly, sun sometimes forgets to set errno in recv
TRC(fprintf(stderr, " recv res=%d, hasread=%d\n", res, hasread));
if (res < 0)
{
- TRC(fprintf(stderr, " recv errno=%d, (%s)\n", yaz_errno(),
+ TRC(fprintf(stderr, " recv errno=%d, (%s)\n", yaz_errno(),
strerror(yaz_errno())));
#ifdef WIN32
if (WSAGetLastError() == WSAEWOULDBLOCK)
break;
}
else
+ {
+ h->cerrno = CSYSERR;
return -1;
+ }
#else
if (yaz_errno() == EWOULDBLOCK
#ifdef EAGAIN
else if (yaz_errno() == 0)
continue;
else
+ {
+ h->cerrno = CSYSERR;
return -1;
+ }
#endif
}
else if (!res)
return hasread;
hasread += res;
+ if (hasread > h->max_recv_bytes)
+ {
+ h->cerrno = CSBUFSIZE;
+ return -1;
+ }
}
TRC (fprintf (stderr, " Out of read loop with hasread=%d, berlen=%d\n",
hasread, berlen));
if (!sp->altbuf)
{
if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
+ {
+ h->cerrno = CSYSERR;
return -1;
+ }
} else if (sp->altsize < req)
if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
+ {
+ h->cerrno = CSYSERR;
return -1;
+ }
TRC(fprintf(stderr, " Moving %d bytes to altbuf(0x%x)\n", tomove,
(unsigned) sp->altbuf));
memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
if (sp->ctx_alloc)
SSL_CTX_free (sp->ctx_alloc);
#endif
+#if HAVE_GETADDRINFO
+ if (sp->ai)
+ freeaddrinfo(sp->ai);
+#endif
xfree(sp);
xfree(h);
return 0;
char *tcpip_addrstr(COMSTACK h)
{
- struct sockaddr_in addr;
tcpip_state *sp = (struct tcpip_state *)h->cprivate;
char *r = 0, *buf = sp->buf;
- YAZ_SOCKLEN_T len;
+
+#if HAVE_GETADDRINFO
+ char host[120];
+ struct sockaddr_storage addr;
+ YAZ_SOCKLEN_T len = sizeof(addr);
+
+ if (getpeername(h->iofile, (struct sockaddr *)&addr, &len) < 0)
+ {
+ h->cerrno = CSYSERR;
+ return 0;
+ }
+ if (getnameinfo((struct sockaddr *) &addr, len, host, sizeof(host)-1,
+ 0, 0,
+ (h->flags & CS_FLAGS_NUMERICHOST) ? NI_NUMERICHOST : 0))
+ {
+ r = "unknown";
+ }
+ else
+ r = host;
+
+#else
+
+ struct sockaddr_in addr;
+ YAZ_SOCKLEN_T len = sizeof(addr);
struct hostent *host;
- len = sizeof(addr);
if (getpeername(h->iofile, (struct sockaddr*) &addr, &len) < 0)
{
h->cerrno = CSYSERR;
return 0;
}
- if (!(h->blocking&2)) {
- if ((host = gethostbyaddr((char*)&addr.sin_addr, sizeof(addr.sin_addr),
- AF_INET)))
+ if (!(h->flags & CS_FLAGS_NUMERICHOST))
+ {
+ if ((host = gethostbyaddr((char*)&addr.sin_addr,
+ sizeof(addr.sin_addr),
+ AF_INET)))
r = (char*) host->h_name;
}
if (!r)
- r = inet_ntoa(addr.sin_addr);
+ r = inet_ntoa(addr.sin_addr);
+#endif
+
if (h->protocol == PROTO_HTTP)
sprintf(buf, "http:%s", r);
else
return buf;
}
-int static tcpip_set_blocking(COMSTACK p, int blocking)
+int static tcpip_set_blocking(COMSTACK p, int flags)
{
unsigned long flag;
- if (p->blocking == blocking)
- return 1;
#ifdef WIN32
- flag = 1;
+ flag = (flags & CS_FLAGS_BLOCKING) ? 0 : 1;
if (ioctlsocket(p->iofile, FIONBIO, &flag) < 0)
return 0;
#else
flag = fcntl(p->iofile, F_GETFL, 0);
- if(!(blocking&1))
- flag = flag & ~O_NONBLOCK;
+ if (flags & CS_FLAGS_BLOCKING)
+ flag = flag & ~O_NONBLOCK; /* blocking */
else
- flag = flag | O_NONBLOCK;
+ {
+ flag = flag | O_NONBLOCK; /* non-blocking */
+ signal(SIGPIPE, SIG_IGN);
+ }
if (fcntl(p->iofile, F_SETFL, flag) < 0)
return 0;
#endif
- p->blocking = blocking;
+ p->flags = flags;
return 1;
}