]> Pileus Git - ~andy/fetchmail/blob - socket.c
Enable build without socketpair.
[~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     if (plugin) 
53     {
54         int fds[2];
55         if (socketpair(AF_UNIX,SOCK_STREAM,0,fds))
56         {
57             error(0, 0, _("fetchmail: socketpair failed: %s(%d)"),strerror(errno),errno);
58             return -1;
59         }
60         if (!fork())
61         {
62             dup2(fds[0],0);
63             dup2(fds[0],1);
64             if (outlevel >= O_VERBOSE)
65                 error(0, 0, _("running %s %s %s"), plugin, host, service);
66             execlp(plugin,plugin,host,service,0);
67             error(0, 0, _("execl(%s) failed: %s (%d)"),
68                   plugin, strerror(errno), errno);
69             exit(0);
70         }
71         return fds[1];
72     }
73 }
74 #endif /* HAVE_SOCKETPAIR */
75
76 #if INET6
77 int SockOpen(const char *host, const char *service, const char *options,
78              const char *plugin)
79 {
80     int i;
81     struct addrinfo *ai, req;
82 #if NET_SECURITY
83     void *request = NULL;
84     int requestlen;
85 #endif /* NET_SECURITY */
86
87 #ifdef HAVE_SOCKETPAIR
88     if (plugin)
89         return handle_plugin(host,service,plugin);
90 #endif /* HAVE_SOCKETPAIR */
91     memset(&req, 0, sizeof(struct addrinfo));
92     req.ai_socktype = SOCK_STREAM;
93
94     if (i = getaddrinfo(host, service, &req, &ai)) {
95         error(0, 0, _("fetchmail: getaddrinfo(%s.%s): %s(%d)"), host, service, gai_strerror(i), i);
96         return -1;
97     };
98
99 #if NET_SECURITY
100     if (!options)
101         requestlen = 0;
102     else
103         if (net_security_strtorequest((char *)options, &request, &requestlen))
104             goto ret;
105
106     i = inner_connect(ai, request, requestlen, NULL, NULL, "fetchmail", NULL);
107     if (request)
108         free(request);
109
110  ret:
111 #else /* NET_SECURITY */
112     i = inner_connect(ai, NULL, 0, NULL, NULL, "fetchmail", NULL);
113 #endif /* NET_SECURITY */
114
115     freeaddrinfo(ai);
116
117     return i;
118 };
119 #else /* INET6 */
120 #ifndef HAVE_INET_ATON
121 #ifndef  INADDR_NONE
122 #ifdef   INADDR_BROADCAST
123 #define  INADDR_NONE    INADDR_BROADCAST
124 #else
125 #define  INADDR_NONE    -1
126 #endif
127 #endif
128 #endif /* HAVE_INET_ATON */
129
130 int SockOpen(const char *host, int clientPort, const char *options,
131              const char *plugin)
132 {
133     int sock;
134 #ifndef HAVE_INET_ATON
135     unsigned long inaddr;
136 #endif /* HAVE_INET_ATON */
137     struct sockaddr_in ad;
138     struct hostent *hp;
139
140 #ifdef HAVE_SOCKETPAIR
141     if (plugin) {
142       char buf[10];
143       sprintf(buf,"%d",clientPort);
144       return handle_plugin(host,buf,plugin);
145     }
146 #endif /* HAVE_SOCKETPAIR */
147
148     memset(&ad, 0, sizeof(ad));
149     ad.sin_family = AF_INET;
150
151     /* we'll accept a quad address */
152 #ifndef HAVE_INET_ATON
153     inaddr = inet_addr(host);
154     if (inaddr != INADDR_NONE)
155         memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
156     else
157 #else
158     if (!inet_aton(host, &ad.sin_addr))
159 #endif /* HAVE_INET_ATON */
160     {
161         hp = gethostbyname(host);
162
163         if (hp == NULL)
164         {
165             errno = 0;
166             return -1;
167         }
168         /*
169          * Add a check to make sure the address has a valid IPv4 or IPv6
170          * length.  This prevents buffer spamming by a broken DNS.
171          */
172         if(hp->h_length != 4 && hp->h_length != 8)
173         {
174             h_errno = errno = 0;
175             error(0, 0, _("fetchmail: illegal address length received for host %s"));
176             return -1;
177         }
178         /*
179          * FIXME: make this work for multihomed hosts.
180          * We're toast if we get back multiple addresses and h_addrs[0]
181          * (aka h_addr) is not one we can actually connect to; this happens
182          * with multi-homed boxen.
183          */
184         memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
185     }
186     ad.sin_port = htons(clientPort);
187     
188     sock = socket(AF_INET, SOCK_STREAM, 0);
189     if (sock < 0)
190     {
191         h_errno = 0;
192         return -1;
193     }
194     if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0)
195     {
196         int olderr = errno;
197         close(sock);
198         h_errno = 0;
199         errno = olderr;
200         return -1;
201     }
202
203     return(sock);
204 }
205 #endif /* INET6 */
206
207
208 #if defined(HAVE_STDARG_H)
209 int SockPrintf(int sock, const char* format, ...)
210 {
211 #else
212 int SockPrintf(sock,format,va_alist)
213 int sock;
214 char *format;
215 va_dcl {
216 #endif
217
218     va_list ap;
219     char buf[8192];
220
221 #if defined(HAVE_STDARG_H)
222     va_start(ap, format) ;
223 #else
224     va_start(ap);
225 #endif
226 #ifdef HAVE_VSNPRINTF
227     vsnprintf(buf, sizeof(buf), format, ap);
228 #else
229     vsprintf(buf, format, ap);
230 #endif
231     va_end(ap);
232     return SockWrite(sock, buf, strlen(buf));
233
234 }
235
236 int SockWrite(int sock, char *buf, int len)
237 {
238     int n, wrlen = 0;
239
240     while (len)
241     {
242         n = write(sock, buf, len);
243         if (n <= 0)
244             return -1;
245         len -= n;
246         wrlen += n;
247         buf += n;
248     }
249     return wrlen;
250 }
251
252 int SockRead(int sock, char *buf, int len)
253 {
254     char *newline, *bp = buf;
255     int n;
256
257     if (--len < 1)
258         return(-1);
259     do {
260         /* 
261          * The reason for these gymnastics is that we want two things:
262          * (1) to read \n-terminated lines,
263          * (2) to return the true length of data read, even if the
264          *     data coming in has embedded NULS.
265          */
266         if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0)
267             return(-1);
268         if ((newline = memchr(bp, '\n', n)) != NULL)
269             n = newline - bp + 1;
270         if ((n = read(sock, bp, n)) == -1)
271             return(-1);
272         bp += n;
273         len -= n;
274     } while 
275             (!newline && len);
276     *bp = '\0';
277     return bp - buf;
278 }
279
280 int SockPeek(int sock)
281 /* peek at the next socket character without actually reading it */
282 {
283     int n;
284     char ch;
285
286     if ((n = recv(sock, &ch, 1, MSG_PEEK)) == -1)
287         return -1;
288     else
289         return(ch);
290 }
291
292 int SockClose(int sock)
293 /* close a socket (someday we may do other cleanup here) */
294 {
295     return(close(sock));
296 }
297
298 #ifdef MAIN
299 /*
300  * Use the chargen service to test input buffering directly.
301  * You may have to uncomment the `chargen' service description in your
302  * inetd.conf (and then SIGHUP inetd) for this to work. 
303  */
304 main()
305 {
306     int         sock = SockOpen("localhost", 19, NULL);
307     char        buf[80];
308
309     while (SockRead(sock, buf, sizeof(buf)-1))
310         SockWrite(1, buf, strlen(buf));
311     SockClose(sock);
312 }
313 #endif /* MAIN */
314
315 /* socket.c ends here */