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