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