]> Pileus Git - ~andy/fetchmail/blob - socket.c
Craig's patches in response to cmetz3.
[~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 <string.h>
10 #ifdef HAVE_MEMORY_H
11 #include <memory.h>
12 #endif /* HAVE_MEMORY_H */
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
17 #include <netdb.h>
18 #if defined(STDC_HEADERS)
19 #include <stdlib.h>
20 #endif
21 #if defined(HAVE_UNISTD_H)
22 #include <unistd.h>
23 #endif
24 #if defined(HAVE_STDARG_H)
25 #include <stdarg.h>
26 #else
27 #include <varargs.h>
28 #endif
29 #include "socket.h"
30
31 #if NET_SECURITY
32 #include <net/security.h>
33 #endif /* NET_SECURITY */
34
35 #if INET6
36 int SockOpen(const char *host, const char *service, const char *options)
37 {
38   int i;
39   struct addrinfo *ai, req;
40 #if NET_SECURITY
41   struct net_security_operation request[NET_SECURITY_OPERATION_MAX];
42   int requestlen;
43 #endif /* NET_SECURITY */
44
45   memset(&req, 0, sizeof(struct addrinfo));
46   req.ai_socktype = SOCK_STREAM;
47
48   if (i = getaddrinfo(host, service, &req, &ai)) {
49     fprintf(stderr, "fetchmail: getaddrinfo(%s.%s): %s(%d)\n", host, service, gai_strerror(i), i);
50     return -1;
51   };
52
53 #if NET_SECURITY
54   if (!options)
55     requestlen = 0;
56   else
57     if (net_security_strtorequest((char *)options, request, &requestlen))
58       goto ret;
59
60   i = inner_connect(ai, request, requestlen, NULL,NULL, "fetchmail", NULL);
61
62 ret:
63 #else /* NET_SECURITY */
64   i = inner_connect(ai, NULL, 0, NULL, NULL, "fetchmail", NULL);
65 #endif /* NET_SECURITY */
66
67   freeaddrinfo(ai);
68
69   return i;
70 };
71 #else /* INET6 */
72 #ifndef INET_ATON
73 #ifndef  INADDR_NONE
74 #ifdef   INADDR_BROADCAST
75 #define  INADDR_NONE    INADDR_BROADCAST
76 #else
77 #define  INADDR_NONE    -1
78 #endif
79 #endif
80 #endif /* INET_ATON */
81
82 int SockOpen(const char *host, int clientPort, const char *options)
83 {
84     int sock;
85 #ifndef INET_ATON
86     unsigned long inaddr;
87 #endif /* INET_ATON */
88     struct sockaddr_in ad;
89     struct hostent *hp;
90
91     memset(&ad, 0, sizeof(ad));
92     ad.sin_family = AF_INET;
93
94     /* we'll accept a quad address */
95 #ifndef INET_ATON
96     inaddr = inet_addr(host);
97     if (inaddr != INADDR_NONE)
98         memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
99     else
100 #else
101     if (!inet_aton(host, &ad.sin_addr))
102 #endif /* INET_ATON */
103     {
104         hp = gethostbyname(host);
105
106         /*
107          * Add a check to make sure the address has a valid IPv4 or IPv6
108          * length.  This prevents buffer spamming by a broken DNS.
109          */
110         if (hp == NULL || (hp->h_length != 4 && hp->h_length != 8))
111             return -1;
112
113         /*
114          * FIXME: make this work for multihomed hosts.
115          * We're toast if we get back multiple addresses and h_addrs[0]
116          * (aka h_addr) is not one we can actually connect to; this happens
117          * with multi-homed boxen.
118          */
119         memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
120     }
121     ad.sin_port = htons(clientPort);
122     
123     sock = socket(AF_INET, SOCK_STREAM, 0);
124     if (sock < 0)
125         return -1;
126     if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0)
127     {
128         close(sock);
129         return -1;
130     }
131
132     return(sock);
133 }
134 #endif /* INET6 */
135
136
137 #if defined(HAVE_STDARG_H)
138 int SockPrintf(int sock, char* format, ...)
139 {
140 #else
141 int SockPrintf(sock,format,va_alist)
142 int sock;
143 char *format;
144 va_dcl {
145 #endif
146
147     va_list ap;
148     char buf[8192];
149
150 #if defined(HAVE_STDARG_H)
151     va_start(ap, format) ;
152 #else
153     va_start(ap);
154 #endif
155 #ifdef HAVE_VSNPRINTF
156     vsnprintf(buf, sizeof(buf), format, ap);
157 #else
158     vsprintf(buf, format, ap);
159 #endif
160     va_end(ap);
161     return SockWrite(sock, buf, strlen(buf));
162
163 }
164
165 int SockWrite(int sock, char *buf, int len)
166 {
167     int n, wrlen = 0;
168
169     while (len)
170     {
171         n = write(sock, buf, len);
172         if (n <= 0)
173             return -1;
174         len -= n;
175         wrlen += n;
176         buf += n;
177     }
178     return wrlen;
179 }
180
181 int SockRead(int sock, char *buf, int len)
182 {
183     char *newline, *bp = buf;
184     int n;
185
186     if (--len < 1)
187         return(-1);
188     do {
189         /* 
190          * The reason for these gymnastics is that we want two things:
191          * (1) to read \n-terminated lines,
192          * (2) to return the true length of data read, even if the
193          *     data coming in has embedded NULS.
194          */
195         if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0)
196             return(-1);
197         if ((newline = memchr(bp, '\n', n)) != NULL)
198             n = newline - bp + 1;
199         if ((n = read(sock, bp, n)) == -1)
200             return(-1);
201         bp += n;
202         len -= n;
203     } while 
204             (!newline && len);
205     *bp = '\0';
206     return bp - buf;
207 }
208
209 int SockPeek(int sock)
210 /* peek at the next socket character without actually reading it */
211 {
212     int n;
213     char ch;
214
215     if ((n = recv(sock, &ch, 1, MSG_PEEK)) == -1)
216         return -1;
217     else
218         return(ch);
219 }
220
221 #ifdef MAIN
222 /*
223  * Use the chargen service to test input beuffering directly.
224  * You may have to uncomment the `chargen' service description in your
225  * inetd.conf (and then SIGHUP inetd) for this to work. 
226  */
227 main()
228 {
229     int         sock = SockOpen("localhost", 19, NULL);
230     char        buf[80];
231
232     while (SockRead(sock, buf, sizeof(buf)-1))
233         SockWrite(1, buf, strlen(buf));
234 }
235 #endif /* MAIN */
236
237 /* socket.c ends here */