Commit | Line | Data |
---|---|---|
ca67e7b4 C |
1 | /* |
2 | * NNTP client routines. | |
3 | */ | |
4 | ||
5 | #ifndef lint | |
6 | static char *sccsid = "@(#)clientlib.c 1.5 (Berkeley) 10/15/87"; | |
7 | #endif | |
8 | ||
9 | #include <stdio.h> | |
10 | #include <sys/types.h> | |
11 | #include <sys/socket.h> | |
12 | #include <netinet/in.h> | |
13 | #include <netdb.h> | |
14 | ||
15 | #if defined(AF_DECnet) && defined(ultrix) | |
16 | # ifndef DECNET | |
17 | # define DECNET | |
18 | # endif | |
19 | #endif | |
20 | ||
21 | #ifdef DECNET | |
22 | #include <netdnet/dn.h> | |
23 | #include <netdnet/dnetdb.h> | |
24 | #endif | |
25 | ||
26 | #include "response_codes.h" | |
27 | ||
28 | FILE *ser_rd_fp = NULL; | |
29 | FILE *ser_wr_fp = NULL; | |
30 | ||
31 | /* | |
32 | * getserverbyfile Get the name of a server from a named file. | |
33 | * Handle white space and comments. | |
34 | * Use NNTPSERVER environment variable if set. | |
35 | * | |
36 | * Parameters: "file" is the name of the file to read. | |
37 | * | |
38 | * Returns: Pointer to static data area containing the | |
39 | * first non-ws/comment line in the file. | |
40 | * NULL on error (or lack of entry in file). | |
41 | * | |
42 | * Side effects: None. | |
43 | */ | |
44 | ||
45 | char * | |
46 | getserverbyfile(file) | |
47 | char *file; | |
48 | { | |
49 | register FILE *fp; | |
50 | register char *cp; | |
51 | static char buf[256]; | |
52 | char *index(); | |
53 | char *getenv(); | |
54 | ||
55 | if (cp = getenv("NNTPSERVER")) { | |
56 | (void) strcpy(buf, cp); | |
57 | return (buf); | |
58 | } | |
59 | ||
60 | if (file == NULL) | |
61 | return (NULL); | |
62 | ||
63 | fp = fopen(file, "r"); | |
64 | if (fp == NULL) | |
65 | return (NULL); | |
66 | ||
67 | while (fgets(buf, sizeof (buf), fp) != NULL) { | |
68 | if (*buf == '\n' || *buf == '#') | |
69 | continue; | |
70 | cp = index(buf, '\n'); | |
71 | if (cp) | |
72 | *cp = '\0'; | |
73 | (void) fclose(fp); | |
74 | return (buf); | |
75 | } | |
76 | ||
77 | (void) fclose(fp); | |
78 | return (NULL); /* No entry */ | |
79 | } | |
80 | ||
81 | ||
82 | /* | |
83 | * server_init Get a connection to the remote news server. | |
84 | * | |
85 | * Parameters: "machine" is the machine to connect to. | |
86 | * | |
87 | * Returns: -1 on error | |
88 | * server's initial response code on success. | |
89 | * | |
90 | * Side effects: Connects to server. | |
91 | * "ser_rd_fp" and "ser_wr_fp" are fp's | |
92 | * for reading and writing to server. | |
93 | */ | |
94 | ||
95 | server_init(machine) | |
96 | char *machine; | |
97 | { | |
98 | int sockt_rd, sockt_wr; | |
99 | char line[256]; | |
100 | char *cp; | |
101 | char *index(); | |
102 | ||
103 | #ifdef DECNET | |
104 | cp = index(machine, ':'); | |
105 | ||
106 | if (cp && cp[1] == ':') { | |
107 | *cp = '\0'; | |
108 | sockt_rd = get_dnet_socket(machine); | |
109 | } else | |
110 | sockt_rd = get_tcp_socket(machine); | |
111 | #else | |
112 | sockt_rd = get_tcp_socket(machine); | |
113 | #endif | |
114 | ||
115 | if (sockt_rd < 0) | |
116 | return (-1); | |
117 | ||
118 | /* | |
119 | * Now we'll make file pointers (i.e., buffered I/O) out of | |
120 | * the socket file descriptor. Note that we can't just | |
121 | * open a fp for reading and writing -- we have to open | |
122 | * up two separate fp's, one for reading, one for writing. | |
123 | */ | |
124 | ||
125 | if ((ser_rd_fp = fdopen(sockt_rd, "r")) == NULL) { | |
126 | perror("server_init: fdopen #1"); | |
127 | return (-1); | |
128 | } | |
129 | ||
130 | sockt_wr = dup(sockt_rd); | |
131 | if ((ser_wr_fp = fdopen(sockt_wr, "w")) == NULL) { | |
132 | perror("server_init: fdopen #2"); | |
133 | ser_rd_fp = NULL; /* from above */ | |
134 | return (-1); | |
135 | } | |
136 | ||
137 | /* Now get the server's signon message */ | |
138 | ||
139 | (void) get_server(line, sizeof(line)); | |
140 | return (atoi(line)); | |
141 | } | |
142 | ||
143 | ||
144 | /* | |
145 | * get_tcp_socket -- get us a socket connected to the news server. | |
146 | * | |
147 | * Parameters: "machine" is the machine the server is running on. | |
148 | * | |
149 | * Returns: Socket connected to the news server if | |
150 | * all is ok, else -1 on error. | |
151 | * | |
152 | * Side effects: Connects to server. | |
153 | * | |
154 | * Errors: Printed via perror. | |
155 | */ | |
156 | ||
157 | get_tcp_socket(machine) | |
158 | char *machine; | |
159 | { | |
160 | int s, x = 0; | |
161 | register char **cp; | |
162 | struct sockaddr_in sin; | |
163 | struct servent *getservbyname(), *sp; | |
164 | struct hostent *gethostbyname(), *hp; | |
165 | ||
166 | if ((sp = getservbyname("nntp", "tcp")) == NULL) { | |
167 | fprintf(stderr, "nntp/tcp: Unknown service.\n"); | |
168 | return (-1); | |
169 | } | |
170 | ||
171 | if ((hp = gethostbyname(machine)) == NULL) { | |
172 | fprintf(stderr, "%s: Unknown host.\n", machine); | |
173 | return (-1); | |
174 | } | |
175 | ||
176 | bzero((char *) &sin, sizeof(sin)); | |
177 | sin.sin_family = hp->h_addrtype; | |
178 | sin.sin_port = sp->s_port; | |
179 | ||
180 | /* | |
181 | * The following is kinda gross. The name server under 4.3 | |
182 | * returns a list of addresses, each of which should be tried | |
183 | * in turn if the previous one fails. However, 4.2 hostent | |
184 | * structure doesn't have this list of addresses. | |
185 | * Under 4.3, h_addr is a #define to h_addr_list[0]. | |
186 | * We use this to figure out whether to include the NS specific | |
187 | * code... | |
188 | */ | |
189 | ||
190 | #ifdef h_addr | |
191 | ||
192 | /* get a socket and initiate connection -- use multiple addresses */ | |
193 | ||
194 | for (cp = hp->h_addr_list; cp && *cp; cp++) { | |
195 | s = socket(hp->h_addrtype, SOCK_STREAM, 0); | |
196 | if (s < 0) { | |
197 | perror("socket"); | |
198 | return (-1); | |
199 | } | |
200 | bcopy(*cp, (char *)&sin.sin_addr, hp->h_length); | |
201 | ||
202 | if (x < 0) | |
203 | fprintf(stderr, "trying %s\n", inet_ntoa(sin.sin_addr)); | |
204 | x = connect(s, (struct sockaddr *)&sin, sizeof (sin)); | |
205 | if (x == 0) | |
206 | break; | |
207 | fprintf(stderr, "connection to %s: ", inet_ntoa(sin.sin_addr)); | |
208 | perror(""); | |
209 | (void) close(s); | |
210 | } | |
211 | if (x < 0) { | |
212 | fprintf(stderr, "giving up...\n"); | |
213 | return (-1); | |
214 | } | |
215 | #else /* no name server */ | |
216 | ||
217 | if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { /* Get the socket */ | |
218 | perror("socket"); | |
219 | return (-1); | |
220 | } | |
221 | ||
222 | /* And then connect */ | |
223 | ||
224 | bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length); | |
225 | if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { | |
226 | perror("connect"); | |
227 | (void) close(s); | |
228 | return (-1); | |
229 | } | |
230 | ||
231 | #endif | |
232 | ||
233 | return (s); | |
234 | } | |
235 | ||
236 | #ifdef DECNET | |
237 | /* | |
238 | * get_dnet_socket -- get us a socket connected to the news server. | |
239 | * | |
240 | * Parameters: "machine" is the machine the server is running on. | |
241 | * | |
242 | * Returns: Socket connected to the news server if | |
243 | * all is ok, else -1 on error. | |
244 | * | |
245 | * Side effects: Connects to server. | |
246 | * | |
247 | * Errors: Printed via nerror. | |
248 | */ | |
249 | ||
250 | get_dnet_socket(machine) | |
251 | char *machine; | |
252 | { | |
253 | int s, area, node; | |
254 | struct sockaddr_dn sdn; | |
255 | struct nodeent *getnodebyname(), *np; | |
256 | ||
257 | bzero((char *) &sdn, sizeof(sdn)); | |
258 | ||
259 | switch (s = sscanf( machine, "%d%*[.]%d", &area, &node )) { | |
260 | case 1: | |
261 | node = area; | |
262 | area = 0; | |
263 | case 2: | |
264 | node += area*1024; | |
265 | sdn.sdn_add.a_len = 2; | |
266 | sdn.sdn_family = AF_DECnet; | |
267 | sdn.sdn_add.a_addr[0] = node % 256; | |
268 | sdn.sdn_add.a_addr[1] = node / 256; | |
269 | break; | |
270 | default: | |
271 | if ((np = getnodebyname(machine)) == NULL) { | |
272 | fprintf(stderr, | |
273 | "%s: Unknown host.\n", machine); | |
274 | return (-1); | |
275 | } else { | |
276 | bcopy(np->n_addr, | |
277 | (char *) sdn.sdn_add.a_addr, | |
278 | np->n_length); | |
279 | sdn.sdn_add.a_len = np->n_length; | |
280 | sdn.sdn_family = np->n_addrtype; | |
281 | } | |
282 | break; | |
283 | } | |
284 | sdn.sdn_objnum = 0; | |
285 | sdn.sdn_flags = 0; | |
286 | sdn.sdn_objnamel = strlen("NNTP"); | |
287 | bcopy("NNTP", &sdn.sdn_objname[0], sdn.sdn_objnamel); | |
288 | ||
289 | if ((s = socket(AF_DECnet, SOCK_STREAM, 0)) < 0) { | |
290 | nerror("socket"); | |
291 | return (-1); | |
292 | } | |
293 | ||
294 | /* And then connect */ | |
295 | ||
296 | if (connect(s, (struct sockaddr *) &sdn, sizeof(sdn)) < 0) { | |
297 | nerror("connect"); | |
298 | close(s); | |
299 | return (-1); | |
300 | } | |
301 | ||
302 | return (s); | |
303 | } | |
304 | #endif | |
305 | ||
306 | ||
307 | ||
308 | /* | |
309 | * handle_server_response | |
310 | * | |
311 | * Print some informative messages based on the server's initial | |
312 | * response code. This is here so inews, rn, etc. can share | |
313 | * the code. | |
314 | * | |
315 | * Parameters: "response" is the response code which the | |
316 | * server sent us, presumably from "server_init", | |
317 | * above. | |
318 | * "server" is the news server we got the | |
319 | * response code from. | |
320 | * | |
321 | * Returns: -1 if the error is fatal (and we should exit). | |
322 | * 0 otherwise. | |
323 | * | |
324 | * Side effects: None. | |
325 | */ | |
326 | ||
327 | handle_server_response(response, server) | |
328 | int response; | |
329 | char *server; | |
330 | { | |
331 | switch (response) { | |
332 | case OK_NOPOST: /* fall through */ | |
333 | printf( | |
334 | "NOTE: This machine does not have permission to post articles.\n"); | |
335 | printf( | |
336 | " Please don't waste your time trying.\n\n"); | |
337 | ||
338 | case OK_CANPOST: | |
339 | return (0); | |
340 | break; | |
341 | ||
342 | case ERR_ACCESS: | |
343 | printf( | |
344 | "This machine does not have permission to use the %s news server.\n", | |
345 | server); | |
346 | return (-1); | |
347 | break; | |
348 | ||
349 | default: | |
350 | printf("Unexpected response code from %s news server: %d\n", | |
351 | server, response); | |
352 | return (-1); | |
353 | break; | |
354 | } | |
355 | /*NOTREACHED*/ | |
356 | } | |
357 | ||
358 | ||
359 | /* | |
360 | * put_server -- send a line of text to the server, terminating it | |
361 | * with CR and LF, as per ARPA standard. | |
362 | * | |
363 | * Parameters: "string" is the string to be sent to the | |
364 | * server. | |
365 | * | |
366 | * Returns: Nothing. | |
367 | * | |
368 | * Side effects: Talks to the server. | |
369 | * | |
370 | * Note: This routine flushes the buffer each time | |
371 | * it is called. For large transmissions | |
372 | * (i.e., posting news) don't use it. Instead, | |
373 | * do the fprintf's yourself, and then a final | |
374 | * fflush. | |
375 | */ | |
376 | ||
377 | void | |
378 | put_server(string) | |
379 | char *string; | |
380 | { | |
381 | #ifdef DEBUG | |
382 | fprintf(stderr, ">>> %s\n", string); | |
383 | #endif | |
384 | fprintf(ser_wr_fp, "%s\r\n", string); | |
385 | (void) fflush(ser_wr_fp); | |
386 | } | |
387 | ||
388 | ||
389 | /* | |
390 | * get_server -- get a line of text from the server. Strips | |
391 | * CR's and LF's. | |
392 | * | |
393 | * Parameters: "string" has the buffer space for the | |
394 | * line received. | |
395 | * "size" is the size of the buffer. | |
396 | * | |
397 | * Returns: -1 on error, 0 otherwise. | |
398 | * | |
399 | * Side effects: Talks to server, changes contents of "string". | |
400 | */ | |
401 | ||
402 | get_server(string, size) | |
403 | char *string; | |
404 | int size; | |
405 | { | |
406 | register char *cp; | |
407 | char *index(); | |
408 | ||
409 | if (fgets(string, size, ser_rd_fp) == NULL) | |
410 | return (-1); | |
411 | ||
412 | if ((cp = index(string, '\r')) != NULL) | |
413 | *cp = '\0'; | |
414 | else if ((cp = index(string, '\n')) != NULL) | |
415 | *cp = '\0'; | |
416 | #ifdef DEBUG | |
417 | fprintf(stderr, "<<< %s\n", string); | |
418 | #endif | |
419 | ||
420 | return (0); | |
421 | } | |
422 | ||
423 | ||
424 | /* | |
425 | * close_server -- close the connection to the server, after sending | |
426 | * the "quit" command. | |
427 | * | |
428 | * Parameters: None. | |
429 | * | |
430 | * Returns: Nothing. | |
431 | * | |
432 | * Side effects: Closes the connection with the server. | |
433 | * You can't use "put_server" or "get_server" | |
434 | * after this routine is called. | |
435 | */ | |
436 | ||
437 | void | |
438 | close_server() | |
439 | { | |
440 | char ser_line[256]; | |
441 | ||
442 | if (ser_wr_fp == NULL || ser_rd_fp == NULL) | |
443 | return; | |
444 | ||
445 | put_server("QUIT"); | |
446 | (void) get_server(ser_line, sizeof(ser_line)); | |
447 | ||
448 | (void) fclose(ser_wr_fp); | |
449 | (void) fclose(ser_rd_fp); | |
450 | } |