3 ** Open a connection to a remote NNTP server.
8 #include "portable/socket.h"
12 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
16 #include "inn/innconf.h"
22 ** store an error message in buff like libinn(3) says NNTPconnect does
24 static void store_error(char *buff, char *string1, char *string2) {
25 /* we put these in [...] to make it easier to tell them apart
26 * from messages sent by the server */
27 snprintf(buff, NNTP_STRLEN, "[%s%s]", string1, string2);
28 buff[NNTP_STRLEN-1] = 0;
32 ** Open a connection to an NNTP server and create stdio FILE's for talking
33 ** to it. Return -1 on error.
35 int NNTPconnect(char *host, int port, FILE **FromServerp, FILE **ToServerp, char *errbuff)
37 char mybuff[NNTP_STRLEN + 2];
45 struct addrinfo hints, *ressave, *addr;
47 struct sockaddr_storage client;
54 struct hostent fakehp;
55 struct in_addr quadaddr;
56 struct sockaddr_in server, client;
59 buff = errbuff ? errbuff : mybuff;
63 memset(&hints, 0, sizeof(struct addrinfo));
64 hints.ai_family = PF_UNSPEC;
65 hints.ai_socktype = SOCK_STREAM;
66 sprintf(portbuf, "%d", port);
67 if ((gai_r = getaddrinfo(host, portbuf, &hints, &addr)) != 0) {
68 store_error(buff, "getaddrinfo failed: ", gai_strerror(gai_r));
73 for (ressave = addr; addr; addr = addr->ai_next) {
74 if ((i = socket(addr->ai_family, addr->ai_socktype,
75 addr->ai_protocol)) < 0)
76 continue; /* ignore */
77 /* bind the local (source) address, if requested */
78 memset(&client, 0, sizeof client);
79 if (addr->ai_family == AF_INET && innconf->sourceaddress) {
80 if (inet_pton(AF_INET, innconf->sourceaddress,
81 &((struct sockaddr_in *)&client)->sin_addr) < 1) {
85 ((struct sockaddr_in *)&client)->sin_family = AF_INET;
86 #ifdef HAVE_SOCKADDR_LEN
87 ((struct sockaddr_in *)&client)->sin_len = sizeof( struct sockaddr_in );
90 if (addr->ai_family == AF_INET6 && innconf->sourceaddress6) {
91 if (inet_pton(AF_INET6, innconf->sourceaddress6,
92 &((struct sockaddr_in6 *)&client)->sin6_addr) < 1) {
96 ((struct sockaddr_in6 *)&client)->sin6_family = AF_INET6;
97 #ifdef HAVE_SOCKADDR_LEN
98 ((struct sockaddr_in6 *)&client)->sin6_len = sizeof( struct sockaddr_in6 );
101 if (client.ss_family != 0) {
102 if (bind(i, (struct sockaddr *)&client, addr->ai_addrlen) < 0) {
107 /* we are ready, try to connect */
108 if (connect(i, addr->ai_addr, addr->ai_addrlen) == 0)
114 freeaddrinfo(ressave);
117 /* all connect(2) calls failed or some other error has occurred */
120 store_error(buff, "connect(s) failed: ", strerror(oerrno));
125 #else /* HAVE_INET6 */
126 if (inet_aton(host, &quadaddr)) {
127 /* Host was specified as a dotted-quad internet address. Fill in
128 * the parts of the hostent struct that we need. */
129 fakehp.h_length = sizeof quadaddr;
130 fakehp.h_addrtype = AF_INET;
132 fakelist[0] = (char *)&quadaddr;
136 else if ((hp = gethostbyname(host)) != NULL)
137 ap = hp->h_addr_list;
139 /* Not a host name. */
140 store_error(buff, "gethostbyname failed: ", hstrerror(h_errno));
145 /* Set up the socket address. */
146 memset(&server, 0, sizeof server);
147 server.sin_family = hp->h_addrtype;
148 #ifdef HAVE_SOCKADDR_LEN
149 server.sin_len = sizeof( struct sockaddr_in );
151 server.sin_port = htons(port);
153 /* Source IP address to which we bind. */
154 memset(&client, 0, sizeof client);
155 client.sin_family = AF_INET;
156 #ifdef HAVE_SOCKADDR_LEN
157 client.sin_len = sizeof( struct sockaddr_in );
159 if (innconf->sourceaddress) {
160 if (!inet_aton(innconf->sourceaddress, &client.sin_addr)) {
161 store_error(buff, "inet_aton failed on innconf's"
162 " source address: ", innconf->sourceaddress);
167 client.sin_addr.s_addr = htonl(INADDR_ANY);
169 /* Loop through the address list, trying to connect. */
170 for (; ap && *ap; ap++) {
171 /* Make a socket and try to connect. */
172 if ((i = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) {
175 store_error(buff, "create socket failed: ", strerror(oerrno));
180 /* Bind to the source address we want. */
181 if (bind(i, (struct sockaddr *)&client, sizeof client) < 0) {
184 store_error(buff, "bind failed: ", strerror(oerrno));
188 /* Copy the address via inline memcpy:
189 * memcpy(&server.sin_addr, *ap, hp->h_length); */
191 for (dest = (char *)&server.sin_addr, j = hp->h_length; --j >= 0; )
193 if (connect(i, (struct sockaddr *)&server, sizeof server) < 0) {
196 store_error(buff, "connect failed: ", strerror(oerrno));
200 #endif /* HAVE_INET6 */
202 /* Connected -- now make sure we can post. */
203 if ((F = fdopen(i, "r")) == NULL) {
207 store_error(buff, "fdopen failed: ", strerror(oerrno));
211 if (fgets(buff, sizeof mybuff, F) == NULL) {
212 if (ferror(F)) store_error(buff, "peer closed connection","");
213 else store_error(buff, "fgets failed: ", strerror(errno));
220 if (j != NNTP_POSTOK_VAL && j != NNTP_NOPOSTOK_VAL) {
222 /* This seems like a reasonable error code to use... */
228 if ((*ToServerp = fdopen(dup(i), "w")) == NULL) {
231 store_error(buff, "fdopen/dup failed: ", strerror(oerrno));
239 store_error(buff, "no address(es) for host","");
244 int NNTPremoteopen(int port, FILE **FromServerp, FILE **ToServerp, char *errbuff)
248 if ((p = innconf->server) == NULL) {
250 strcpy(errbuff, "What server?");
253 return NNTPconnect(p, port, FromServerp, ToServerp, errbuff);