X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=socket.c;h=3ba586962b0f2428f7b842ee2619873791caa217;hb=e5401c14b6fc36b24f7c4411cb447722faed8389;hp=7a98f83fbc6c091ab2cc2a880b2a977cf35e83a4;hpb=f773cf68c2b60a003966d8b69a6f90536fad8bd1;p=~andy%2Ffetchmail diff --git a/socket.c b/socket.c index 7a98f83f..3ba58696 100644 --- a/socket.c +++ b/socket.c @@ -4,17 +4,18 @@ * For license terms, see the file COPYING in this directory. */ -#include - +#include "config.h" #include -#include +#include +#ifdef HAVE_MEMORY_H +#include +#endif /* HAVE_MEMORY_H */ #include #include #include #include #include #if defined(STDC_HEADERS) -#include #include #endif #if defined(HAVE_UNISTD_H) @@ -25,48 +26,122 @@ #else #include #endif -#include #include "socket.h" -FILE *Socket(host, clientPort) -char *host; -int clientPort; +#if NET_SECURITY +#include +#endif /* NET_SECURITY */ + +#if INET6 +int SockOpen(const char *host, const char *service, const char *options) +{ + int i; + struct addrinfo *ai, req; +#if NET_SECURITY + void *request = NULL; + int requestlen; +#endif /* NET_SECURITY */ + + memset(&req, 0, sizeof(struct addrinfo)); + req.ai_socktype = SOCK_STREAM; + + if (i = getaddrinfo(host, service, &req, &ai)) { + fprintf(stderr, "fetchmail: getaddrinfo(%s.%s): %s(%d)\n", host, service, gai_strerror(i), i); + return -1; + }; + +#if NET_SECURITY + if (!options) + requestlen = 0; + else + if (net_security_strtorequest((char *)options, &request, &requestlen)) + goto ret; + + i = inner_connect(ai, request, requestlen, NULL,NULL, "fetchmail", NULL); + if (request) + free(request); + +ret: +#else /* NET_SECURITY */ + i = inner_connect(ai, NULL, 0, NULL, NULL, "fetchmail", NULL); +#endif /* NET_SECURITY */ + + freeaddrinfo(ai); + + return i; +}; +#else /* INET6 */ +#ifndef INET_ATON +#ifndef INADDR_NONE +#ifdef INADDR_BROADCAST +#define INADDR_NONE INADDR_BROADCAST +#else +#define INADDR_NONE -1 +#endif +#endif +#endif /* INET_ATON */ + +int SockOpen(const char *host, int clientPort, const char *options) { int sock; +#ifndef INET_ATON unsigned long inaddr; +#endif /* INET_ATON */ struct sockaddr_in ad; struct hostent *hp; - + memset(&ad, 0, sizeof(ad)); ad.sin_family = AF_INET; + /* we'll accept a quad address */ +#ifndef INET_ATON inaddr = inet_addr(host); if (inaddr != INADDR_NONE) memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr)); else +#else + if (!inet_aton(host, &ad.sin_addr)) +#endif /* INET_ATON */ { hp = gethostbyname(host); - if (hp == NULL) - return (FILE *)NULL; + + /* + * Add a check to make sure the address has a valid IPv4 or IPv6 + * length. This prevents buffer spamming by a broken DNS. + */ + if (hp == NULL || (hp->h_length != 4 && hp->h_length != 8)) + return -1; + + /* + * FIXME: make this work for multihomed hosts. + * We're toast if we get back multiple addresses and h_addrs[0] + * (aka h_addr) is not one we can actually connect to; this happens + * with multi-homed boxen. + */ memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); } ad.sin_port = htons(clientPort); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) - return (FILE *)NULL; + return -1; if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0) - return (FILE *)NULL; - return fdopen(sock, "r+"); + { + close(sock); + return -1; + } + + return(sock); } +#endif /* INET6 */ #if defined(HAVE_STDARG_H) -int SockPrintf(FILE *sockfp, char* format, ...) +int SockPrintf(int sock, char* format, ...) { #else -int SockPrintf(sockfp,format,va_alist) -FILE *sockfp; +int SockPrintf(sock,format,va_alist) +int sock; char *format; va_dcl { #endif @@ -79,44 +154,23 @@ va_dcl { #else va_start(ap); #endif +#ifdef HAVE_VSNPRINTF + vsnprintf(buf, sizeof(buf), format, ap); +#else vsprintf(buf, format, ap); +#endif va_end(ap); - return SockWrite(buf, strlen(buf), sockfp); + return SockWrite(sock, buf, strlen(buf)); } -/* - * FIXME: This needs to be recoded to use stdio, if that's possible. - * - * If you think these functions are too slow and inefficient, you're - * absolutely right. I wish I could figure out what to do about it. - * The ancestral popclient used static buffering here to cut down on the - * number of read(2) calls, but we can't do that because we can have - * two or more sockets open at a time. - * - * The right thing to do would be to use stdio for internal per-socket - * buffering here (which is why Socket() returns a file pointer) but - * this causes mysterious lossage. In case someone ever finds a way - * around this, a note on Carl Harris's original implementation said: - * - * Size of buffer for internal buffering read function - * don't increase beyond the maximum atomic read/write size for - * your sockets, or you'll take a potentially huge performance hit - * - * #define INTERNAL_BUFSIZE 2048 - * - */ - -int SockWrite(buf,len,sockfp) -char *buf; -int len; -FILE *sockfp; +int SockWrite(int sock, char *buf, int len) { int n, wrlen = 0; - + while (len) { - n = write(fileno(sockfp), buf, len); + n = write(sock, buf, len); if (n <= 0) return -1; len -= n; @@ -126,26 +180,60 @@ FILE *sockfp; return wrlen; } -int SockGets(buf, len, sockfp) -char *buf; -int len; -FILE *sockfp; +int SockRead(int sock, char *buf, int len) { - int rdlen = 0; + char *newline, *bp = buf; + int n; - while (--len) - { - if (read(fileno(sockfp), buf, 1) != 1) - return -1; - else - rdlen++; - if (*buf == '\n') - break; - if (*buf != '\r') /* remove all CRs */ - buf++; - } - *buf = 0; - return rdlen; + if (--len < 1) + return(-1); + do { + /* + * The reason for these gymnastics is that we want two things: + * (1) to read \n-terminated lines, + * (2) to return the true length of data read, even if the + * data coming in has embedded NULS. + */ + if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0) + return(-1); + if ((newline = memchr(bp, '\n', n)) != NULL) + n = newline - bp + 1; + if ((n = read(sock, bp, n)) == -1) + return(-1); + bp += n; + len -= n; + } while + (!newline && len); + *bp = '\0'; + return bp - buf; +} + +int SockPeek(int sock) +/* peek at the next socket character without actually reading it */ +{ + int n; + char ch; + + if ((n = recv(sock, &ch, 1, MSG_PEEK)) == -1) + return -1; + else + return(ch); +} + +#ifdef MAIN +/* + * Use the chargen service to test input beuffering directly. + * You may have to uncomment the `chargen' service description in your + * inetd.conf (and then SIGHUP inetd) for this to work. + */ +main() +{ + int sock = SockOpen("localhost", 19, NULL); + char buf[80]; + + while (SockRead(sock, buf, sizeof(buf)-1)) + SockWrite(1, buf, strlen(buf)); } +#endif /* MAIN */ /* socket.c ends here */