]> Pileus Git - ~andy/fetchmail/blob - socket.c
Michael Warfield's IPv6 fixes.
[~andy/fetchmail] / socket.c
1 /*
2  * socket.c -- socket library functions
3  *
4  * Copyright 1998 by Eric S. Raymond.
5  * For license terms, see the file COPYING in this directory.
6  */
7
8 #include "config.h"
9 #include <stdio.h>
10 #include <errno.h>
11 #include <string.h>
12 #ifdef HAVE_MEMORY_H
13 #include <memory.h>
14 #endif /* HAVE_MEMORY_H */
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 #include <netdb.h>
20 #if defined(STDC_HEADERS)
21 #include <stdlib.h>
22 #endif
23 #if defined(HAVE_UNISTD_H)
24 #include <unistd.h>
25 #endif
26 #if defined(HAVE_STDARG_H)
27 #include <stdarg.h>
28 #else
29 #include <varargs.h>
30 #endif
31 #include "socket.h"
32 #include "fetchmail.h"
33 #include "i18n.h"
34
35 #ifdef HAVE_RES_SEARCH
36 /* some versions of FreeBSD should declare this but don't */
37 extern int h_errno;
38 #else
39 /* pretend we have h_errno to avoid some #ifdef's later */
40 static int h_errno;
41 #endif
42
43 #if NET_SECURITY
44 #include <net/security.h>
45 #endif /* NET_SECURITY */
46
47 #ifdef HAVE_SOCKETPAIR
48 static int handle_plugin(const char *host,
49                          const char *service, const char *plugin)
50 /* get a socket mediated through a given external command */
51 {
52     int fds[2];
53     if (socketpair(AF_UNIX,SOCK_STREAM,0,fds))
54     {
55         error(0, 0, _("fetchmail: socketpair failed: %s(%d)"),strerror(errno),errno);
56         return -1;
57     }
58     switch (fork()) {
59         case -1:
60                 /* error */
61                 error(0, 0, _("fetchmail: fork failed: %s(%d)"),
62                                                 strerror(errno), errno);
63                 return -1;
64                 break;
65         case 0: /* child */
66                 /* fds[1] is the parent's end; close it for proper EOF
67                 ** detection */
68                 (void) close(fds[1]);
69                 if ( (dup2(fds[0],0) == -1) || (dup2(fds[0],1) == -1) ) {
70                         error(0, 0, _("fetchmail: dup2 failed: %s(%d)"),
71                                                 strerror(errno), errno);
72                         exit(1);
73                 }
74                 /* fds[0] is now connected to 0 and 1; close it */
75                 (void) close(fds[0]);
76                 if (outlevel >= O_VERBOSE)
77                     error(0, 0, _("running %s %s %s"), plugin, host, service);
78                 execlp(plugin,plugin,host,service,0);
79                 error(0, 0, _("execl(%s) failed: %s (%d)"),
80                       plugin, strerror(errno), errno);
81                 exit(0);
82                 break;
83         default:        /* parent */
84                 /* NOP */
85                 break;
86     }
87     /* fds[0] is the child's end; close it for proper EOF detection */
88     (void) close(fds[0]);
89     return fds[1];
90 }
91 #endif /* HAVE_SOCKETPAIR */
92
93 #if INET6
94 int SockOpen(const char *host, const char *service, const char *options,
95              const char *plugin)
96 {
97     int i;
98     struct addrinfo *ai, req;
99 #if NET_SECURITY
100     void *request = NULL;
101     int requestlen;
102 #endif /* NET_SECURITY */
103
104 #ifdef HAVE_SOCKETPAIR
105     if (plugin)
106         return handle_plugin(host,service,plugin);
107 #endif /* HAVE_SOCKETPAIR */
108     memset(&req, 0, sizeof(struct addrinfo));
109     req.ai_socktype = SOCK_STREAM;
110
111     if (i = getaddrinfo(host, service, &req, &ai)) {
112         error(0, 0, _("fetchmail: getaddrinfo(%s.%s): %s(%d)"), host, service, gai_strerror(i), i);
113         return -1;
114     };
115
116 #if NET_SECURITY
117     if (!options)
118         requestlen = 0;
119     else
120         if (net_security_strtorequest((char *)options, &request, &requestlen))
121             goto ret;
122
123     i = inner_connect(ai, request, requestlen, NULL, NULL, "fetchmail", NULL);
124     if (request)
125         free(request);
126
127  ret:
128 #else /* NET_SECURITY */
129     i = inner_connect(ai, NULL, 0, NULL, NULL, "fetchmail", NULL);
130 #endif /* NET_SECURITY */
131
132     freeaddrinfo(ai);
133
134     return i;
135 };
136 #else /* INET6 */
137 #ifndef HAVE_INET_ATON
138 #ifndef  INADDR_NONE
139 #ifdef   INADDR_BROADCAST
140 #define  INADDR_NONE    INADDR_BROADCAST
141 #else
142 #define  INADDR_NONE    -1
143 #endif
144 #endif
145 #endif /* HAVE_INET_ATON */
146
147 int SockOpen(const char *host, int clientPort, const char *options,
148              const char *plugin)
149 {
150     int sock;
151 #ifndef HAVE_INET_ATON
152     unsigned long inaddr;
153 #endif /* HAVE_INET_ATON */
154     struct sockaddr_in ad;
155     struct hostent *hp;
156
157 #ifdef HAVE_SOCKETPAIR
158     if (plugin) {
159       char buf[10];
160       sprintf(buf,"%d",clientPort);
161       return handle_plugin(host,buf,plugin);
162     }
163 #endif /* HAVE_SOCKETPAIR */
164
165     memset(&ad, 0, sizeof(ad));
166     ad.sin_family = AF_INET;
167
168     /* we'll accept a quad address */
169 #ifndef HAVE_INET_ATON
170     inaddr = inet_addr(host);
171     if (inaddr != INADDR_NONE)
172         memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
173     else
174 #else
175     if (!inet_aton(host, &ad.sin_addr))
176 #endif /* HAVE_INET_ATON */
177     {
178         hp = gethostbyname(host);
179
180         if (hp == NULL)
181         {
182             errno = 0;
183             return -1;
184         }
185         /*
186          * Add a check to make sure the address has a valid IPv4 or IPv6
187          * length.  This prevents buffer spamming by a broken DNS.
188          */
189         if(hp->h_length != 4 && hp->h_length != 8)
190         {
191             h_errno = errno = 0;
192             error(0, 0, _("fetchmail: illegal address length received for host %s"),host);
193             return -1;
194         }
195         /*
196          * FIXME: make this work for multihomed hosts.
197          * We're toast if we get back multiple addresses and h_addrs[0]
198          * (aka h_addr) is not one we can actually connect to; this happens
199          * with multi-homed boxen.
200          */
201         memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
202     }
203     ad.sin_port = htons(clientPort);
204     
205     sock = socket(AF_INET, SOCK_STREAM, 0);
206     if (sock < 0)
207     {
208         h_errno = 0;
209         return -1;
210     }
211     if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0)
212     {
213         int olderr = errno;
214         close(sock);
215         h_errno = 0;
216         errno = olderr;
217         return -1;
218     }
219
220     return(sock);
221 }
222 #endif /* INET6 */
223
224
225 #if defined(HAVE_STDARG_H)
226 int SockPrintf(int sock, const char* format, ...)
227 {
228 #else
229 int SockPrintf(sock,format,va_alist)
230 int sock;
231 char *format;
232 va_dcl {
233 #endif
234
235     va_list ap;
236     char buf[8192];
237
238 #if defined(HAVE_STDARG_H)
239     va_start(ap, format) ;
240 #else
241     va_start(ap);
242 #endif
243 #ifdef HAVE_VSNPRINTF
244     vsnprintf(buf, sizeof(buf), format, ap);
245 #else
246     vsprintf(buf, format, ap);
247 #endif
248     va_end(ap);
249     return SockWrite(sock, buf, strlen(buf));
250
251 }
252
253 int SockWrite(int sock, char *buf, int len)
254 {
255     int n, wrlen = 0;
256
257     while (len)
258     {
259         n = write(sock, buf, len);
260         if (n <= 0)
261             return -1;
262         len -= n;
263         wrlen += n;
264         buf += n;
265     }
266     return wrlen;
267 }
268
269 int SockRead(int sock, char *buf, int len)
270 {
271     char *newline, *bp = buf;
272     int n;
273
274     if (--len < 1)
275         return(-1);
276     do {
277         /* 
278          * The reason for these gymnastics is that we want two things:
279          * (1) to read \n-terminated lines,
280          * (2) to return the true length of data read, even if the
281          *     data coming in has embedded NULS.
282          */
283         if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0)
284             return(-1);
285         if ((newline = memchr(bp, '\n', n)) != NULL)
286             n = newline - bp + 1;
287         if ((n = read(sock, bp, n)) == -1)
288             return(-1);
289         bp += n;
290         len -= n;
291     } while 
292             (!newline && len);
293     *bp = '\0';
294     return bp - buf;
295 }
296
297 int SockPeek(int sock)
298 /* peek at the next socket character without actually reading it */
299 {
300     int n;
301     char ch;
302
303     if ((n = recv(sock, &ch, 1, MSG_PEEK)) == -1)
304         return -1;
305     else
306         return(ch);
307 }
308
309 int SockClose(int sock)
310 /* close a socket (someday we may do other cleanup here) */
311 {
312     return(close(sock));
313 }
314
315 #ifdef MAIN
316 /*
317  * Use the chargen service to test input buffering directly.
318  * You may have to uncomment the `chargen' service description in your
319  * inetd.conf (and then SIGHUP inetd) for this to work. 
320  */
321 main()
322 {
323     int         sock = SockOpen("localhost", 19, NULL);
324     char        buf[80];
325
326     while (SockRead(sock, buf, sizeof(buf)-1))
327         SockWrite(1, buf, strlen(buf));
328     SockClose(sock);
329 }
330 #endif /* MAIN */
331
332 /* socket.c ends here */