chiark / gitweb /
some todos
[inn-innduct.git] / lib / remopen.c
1 /*  $Revision: 6119 $
2 **
3 **  Open a connection to a remote NNTP server.
4 */
5
6 #include "config.h"
7 #include "clibrary.h"
8 #include "portable/socket.h"
9 #include <errno.h>
10 #include <netdb.h>
11
12 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
13 # include <sys/un.h>
14 #endif
15
16 #include "inn/innconf.h"
17 #include "libinn.h"
18 #include "nntp.h"
19 #include "paths.h"
20
21 /*
22 **  Open a connection to an NNTP server and create stdio FILE's for talking
23 **  to it.  Return -1 on error.
24 */
25 int NNTPconnect(char *host, int port, FILE **FromServerp, FILE **ToServerp, char *errbuff)
26 {
27     char                mybuff[NNTP_STRLEN + 2];
28     char                *buff;
29     int                 i = -1;
30     int                 j;
31     int                 oerrno;
32     FILE                *F;
33 #ifdef HAVE_INET6
34     struct addrinfo     hints, *ressave, *addr;
35     char                portbuf[16];
36     struct sockaddr_storage client;
37 #else
38     char                **ap;
39     char                *dest;
40     char                *fakelist[2];
41     char                *p;
42     struct hostent      *hp;
43     struct hostent      fakehp;
44     struct in_addr      quadaddr;
45     struct sockaddr_in  server, client;
46 #endif
47
48     buff = errbuff ? errbuff : mybuff;
49     *buff = '\0';
50
51 #ifdef HAVE_INET6
52     memset(&hints, 0, sizeof(struct addrinfo));
53     hints.ai_family = PF_UNSPEC;
54     hints.ai_socktype = SOCK_STREAM;
55     sprintf(portbuf, "%d", port);
56     if (getaddrinfo(host, portbuf, &hints, &addr) != 0)
57         return -1;
58
59     for (ressave = addr; addr; addr = addr->ai_next) {
60         if ((i = socket(addr->ai_family, addr->ai_socktype,
61                         addr->ai_protocol)) < 0)
62             continue; /* ignore */
63         /* bind the local (source) address, if requested */
64         memset(&client, 0, sizeof client);
65         if (addr->ai_family == AF_INET && innconf->sourceaddress) {
66             if (inet_pton(AF_INET, innconf->sourceaddress,
67                         &((struct sockaddr_in *)&client)->sin_addr) < 1) {
68                 addr = NULL;
69                 break;
70             }
71             ((struct sockaddr_in *)&client)->sin_family = AF_INET;
72 #ifdef HAVE_SOCKADDR_LEN
73             ((struct sockaddr_in *)&client)->sin_len = sizeof( struct sockaddr_in );
74 #endif
75         }
76         if (addr->ai_family == AF_INET6 && innconf->sourceaddress6) {
77             if (inet_pton(AF_INET6, innconf->sourceaddress6,
78                         &((struct sockaddr_in6 *)&client)->sin6_addr) < 1) {
79                 addr = NULL;
80                 break;
81             }
82             ((struct sockaddr_in6 *)&client)->sin6_family = AF_INET6;
83 #ifdef HAVE_SOCKADDR_LEN
84             ((struct sockaddr_in6 *)&client)->sin6_len = sizeof( struct sockaddr_in6 );
85 #endif
86         }
87         if (client.ss_family != 0) {
88             if (bind(i, (struct sockaddr *)&client, addr->ai_addrlen) < 0) {
89                 addr = NULL;
90                 break;
91             }
92         }
93         /* we are ready, try to connect */
94         if (connect(i, addr->ai_addr, addr->ai_addrlen) == 0)
95             break; /* success */
96         oerrno = errno;
97         close(i);
98         errno = oerrno;
99     }
100     freeaddrinfo(ressave);
101
102     if (addr == NULL) {
103         /* all connect(2) calls failed or some other error has occurred */
104         oerrno = errno;
105         close(i);
106         errno = oerrno;
107         return -1;
108     }
109     {
110 #else /* HAVE_INET6 */
111     if (inet_aton(host, &quadaddr)) {
112         /* Host was specified as a dotted-quad internet address.  Fill in
113          * the parts of the hostent struct that we need. */
114         fakehp.h_length = sizeof quadaddr;
115         fakehp.h_addrtype = AF_INET;
116         hp = &fakehp;
117         fakelist[0] = (char *)&quadaddr;
118         fakelist[1] = NULL;
119         ap = fakelist;
120     }
121     else if ((hp = gethostbyname(host)) != NULL)
122         ap = hp->h_addr_list;
123     else
124         /* Not a host name. */
125         return -1;
126
127     /* Set up the socket address. */
128     memset(&server, 0, sizeof server);
129     server.sin_family = hp->h_addrtype;
130 #ifdef HAVE_SOCKADDR_LEN
131     server.sin_len = sizeof( struct sockaddr_in );
132 #endif
133     server.sin_port = htons(port);
134
135     /* Source IP address to which we bind. */
136     memset(&client, 0, sizeof client);
137     client.sin_family = AF_INET;
138 #ifdef HAVE_SOCKADDR_LEN
139     client.sin_len = sizeof( struct sockaddr_in );
140 #endif
141     if (innconf->sourceaddress) {
142         if (!inet_aton(innconf->sourceaddress, &client.sin_addr))
143             return -1;
144     } else
145         client.sin_addr.s_addr = htonl(INADDR_ANY);
146   
147     /* Loop through the address list, trying to connect. */
148     for (; ap && *ap; ap++) {
149         /* Make a socket and try to connect. */
150         if ((i = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)
151             break;
152         /* Bind to the source address we want. */
153         if (bind(i, (struct sockaddr *)&client, sizeof client) < 0) {
154             oerrno = errno;
155             close(i);
156             errno = oerrno;
157             continue;
158         }
159         /* Copy the address via inline memcpy:
160          *      memcpy(&server.sin_addr, *ap, hp->h_length); */
161         p = (char *)*ap;
162         for (dest = (char *)&server.sin_addr, j = hp->h_length; --j >= 0; )
163             *dest++ = *p++;
164         if (connect(i, (struct sockaddr *)&server, sizeof server) < 0) {
165             oerrno = errno;
166             close(i);
167             errno = oerrno;
168             continue;
169         }
170 #endif /* HAVE_INET6 */
171
172         /* Connected -- now make sure we can post. */
173         if ((F = fdopen(i, "r")) == NULL) {
174             oerrno = errno;
175             close(i);
176             errno = oerrno;
177             return -1;
178         }
179         if (fgets(buff, sizeof mybuff, F) == NULL) {
180             oerrno = errno;
181             fclose(F);
182             errno = oerrno;
183             return -1;
184         }
185         j = atoi(buff);
186         if (j != NNTP_POSTOK_VAL && j != NNTP_NOPOSTOK_VAL) {
187             fclose(F);
188             /* This seems like a reasonable error code to use... */
189             errno = EPERM;
190             return -1;
191         }
192
193         *FromServerp = F;
194         if ((*ToServerp = fdopen(dup(i), "w")) == NULL) {
195             oerrno = errno;
196             fclose(F);
197             errno = oerrno;
198             return -1;
199         }
200         return 0;
201     }
202
203     return -1;
204 }
205
206 int NNTPremoteopen(int port, FILE **FromServerp, FILE **ToServerp, char *errbuff)
207 {
208     char                *p;
209
210     if ((p = innconf->server) == NULL) {
211         if (errbuff)
212             strcpy(errbuff, "What server?");
213         return -1;
214     }
215     return NNTPconnect(p, port, FromServerp, ToServerp, errbuff);
216 }