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