tftp: convert IPv6-mapped IPv4 addresses to IPv4
[tftp-hpa.git] / common / tftpsubs.c
blob8c999f66eed84465a37377ff8151257656ad9fd6
1 /*
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "tftpsubs.h"
36 /* Simple minded read-ahead/write-behind subroutines for tftp user and
37 server. Written originally with multiple buffers in mind, but current
38 implementation has two buffer logic wired in.
40 Todo: add some sort of final error check so when the write-buffer
41 is finally flushed, the caller can detect if the disk filled up
42 (or had an i/o error) and return a nak to the other side.
44 Jim Guyton 10/85
47 #include <sys/ioctl.h>
49 #define PKTSIZE MAX_SEGSIZE+4 /* should be moved to tftp.h */
51 int segsize = SEGSIZE; /* Default segsize */
53 struct bf {
54 int counter; /* size of data in buffer, or flag */
55 char buf[PKTSIZE]; /* room for data packet */
56 } bfs[2];
58 /* Values for bf.counter */
59 #define BF_ALLOC -3 /* alloc'd but not yet filled */
60 #define BF_FREE -2 /* free */
61 /* [-1 .. segsize] = size of data in the data buffer */
63 static int nextone; /* index of next buffer to use */
64 static int current; /* index of buffer in use */
66 /* control flags for crlf conversions */
67 int newline = 0; /* fillbuf: in middle of newline expansion */
68 int prevchar = -1; /* putbuf: previous char (cr check) */
70 static struct tftphdr *rw_init(int);
72 struct tftphdr *w_init()
74 return rw_init(0);
75 } /* write-behind */
77 struct tftphdr *r_init()
79 return rw_init(1);
80 } /* read-ahead */
82 /* init for either read-ahead or write-behind */
83 /* x == zero for write-behind, one for read-head */
84 static struct tftphdr *rw_init(int x)
86 newline = 0; /* init crlf flag */
87 prevchar = -1;
88 bfs[0].counter = BF_ALLOC; /* pass out the first buffer */
89 current = 0;
90 bfs[1].counter = BF_FREE;
91 nextone = x; /* ahead or behind? */
92 return (struct tftphdr *)bfs[0].buf;
95 /* Have emptied current buffer by sending to net and getting ack.
96 Free it and return next buffer filled with data.
98 int readit(FILE * file, struct tftphdr **dpp, int convert)
100 struct bf *b;
102 bfs[current].counter = BF_FREE; /* free old one */
103 current = !current; /* "incr" current */
105 b = &bfs[current]; /* look at new buffer */
106 if (b->counter == BF_FREE) /* if it's empty */
107 read_ahead(file, convert); /* fill it */
108 /* assert(b->counter != BF_FREE);*//* check */
109 *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */
110 return b->counter;
114 * fill the input buffer, doing ascii conversions if requested
115 * conversions are lf -> cr,lf and cr -> cr, nul
117 void read_ahead(FILE * file, int convert)
119 int i;
120 char *p;
121 int c;
122 struct bf *b;
123 struct tftphdr *dp;
125 b = &bfs[nextone]; /* look at "next" buffer */
126 if (b->counter != BF_FREE) /* nop if not free */
127 return;
128 nextone = !nextone; /* "incr" next buffer ptr */
130 dp = (struct tftphdr *)b->buf;
132 if (convert == 0) {
133 b->counter = read(fileno(file), dp->th_data, segsize);
134 return;
137 p = dp->th_data;
138 for (i = 0; i < segsize; i++) {
139 if (newline) {
140 if (prevchar == '\n')
141 c = '\n'; /* lf to cr,lf */
142 else
143 c = '\0'; /* cr to cr,nul */
144 newline = 0;
145 } else {
146 c = getc(file);
147 if (c == EOF)
148 break;
149 if (c == '\n' || c == '\r') {
150 prevchar = c;
151 c = '\r';
152 newline = 1;
155 *p++ = c;
157 b->counter = (int)(p - dp->th_data);
160 /* Update count associated with the buffer, get new buffer
161 from the queue. Calls write_behind only if next buffer not
162 available.
164 int writeit(FILE * file, struct tftphdr **dpp, int ct, int convert)
166 bfs[current].counter = ct; /* set size of data to write */
167 current = !current; /* switch to other buffer */
168 if (bfs[current].counter != BF_FREE) /* if not free */
169 (void)write_behind(file, convert); /* flush it */
170 bfs[current].counter = BF_ALLOC; /* mark as alloc'd */
171 *dpp = (struct tftphdr *)bfs[current].buf;
172 return ct; /* this is a lie of course */
176 * Output a buffer to a file, converting from netascii if requested.
177 * CR,NUL -> CR and CR,LF => LF.
178 * Note spec is undefined if we get CR as last byte of file or a
179 * CR followed by anything else. In this case we leave it alone.
181 int write_behind(FILE * file, int convert)
183 char *buf;
184 int count;
185 int ct;
186 char *p;
187 int c; /* current character */
188 struct bf *b;
189 struct tftphdr *dp;
191 b = &bfs[nextone];
192 if (b->counter < -1) /* anything to flush? */
193 return 0; /* just nop if nothing to do */
195 count = b->counter; /* remember byte count */
196 b->counter = BF_FREE; /* reset flag */
197 dp = (struct tftphdr *)b->buf;
198 nextone = !nextone; /* incr for next time */
199 buf = dp->th_data;
201 if (count <= 0)
202 return -1; /* nak logic? */
204 if (convert == 0)
205 return write(fileno(file), buf, count);
207 p = buf;
208 ct = count;
209 while (ct--) { /* loop over the buffer */
210 c = *p++; /* pick up a character */
211 if (prevchar == '\r') { /* if prev char was cr */
212 if (c == '\n') /* if have cr,lf then just */
213 fseek(file, -1, 1); /* smash lf on top of the cr */
214 else if (c == '\0') /* if have cr,nul then */
215 goto skipit; /* just skip over the putc */
216 /* else just fall through and allow it */
218 putc(c, file);
219 skipit:
220 prevchar = c;
222 return count;
225 /* When an error has occurred, it is possible that the two sides
226 * are out of synch. Ie: that what I think is the other side's
227 * response to packet N is really their response to packet N-1.
229 * So, to try to prevent that, we flush all the input queued up
230 * for us on the network connection on our host.
232 * We return the number of packets we flushed (mostly for reporting
233 * when trace is active).
236 int synchnet(int f)
237 { /* socket to flush */
238 int pktcount = 0;
239 char rbuf[PKTSIZE];
240 union sock_addr from;
241 socklen_t fromlen;
242 fd_set socketset;
243 struct timeval notime;
245 while (1) {
246 notime.tv_sec = notime.tv_usec = 0;
248 FD_ZERO(&socketset);
249 FD_SET(f, &socketset);
251 if (select(f, &socketset, NULL, NULL, &notime) <= 0)
252 break; /* Nothing to read */
254 /* Otherwise drain the packet */
255 pktcount++;
256 fromlen = sizeof(from);
257 (void)recvfrom(f, rbuf, sizeof(rbuf), 0,
258 &from.sa, &fromlen);
261 return pktcount; /* Return packets drained */
264 int pick_port_bind(int sockfd, union sock_addr *myaddr,
265 unsigned int port_range_from,
266 unsigned int port_range_to)
268 unsigned int port, firstport;
269 int port_range = 0;
271 if (port_range_from != 0 && port_range_to != 0) {
272 port_range = 1;
275 firstport = port_range
276 ? port_range_from + rand() % (port_range_to - port_range_from + 1)
277 : 0;
279 port = firstport;
281 do {
282 sa_set_port(myaddr, htons(port));
283 if (bind(sockfd, &myaddr->sa, SOCKLEN(myaddr)) < 0) {
284 /* Some versions of Linux return EINVAL instead of EADDRINUSE */
285 if (!(port_range && (errno == EINVAL || errno == EADDRINUSE)))
286 return -1;
288 /* Normally, we shouldn't have to loop, but some situations involving
289 aborted transfers make it possible. */
290 } else {
291 return 0;
294 port++;
295 if (port > port_range_to)
296 port = port_range_from;
297 } while (port != firstport);
299 return -1;
303 set_sock_addr(char *host,union sock_addr *s, char **name)
305 struct addrinfo *addrResult;
306 struct addrinfo hints;
307 int err;
309 memset(&hints, 0, sizeof(hints));
310 hints.ai_family = s->sa.sa_family;
311 hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
312 hints.ai_socktype = SOCK_DGRAM;
313 hints.ai_protocol = IPPROTO_UDP;
314 err = getaddrinfo(strip_address(host), NULL, &hints, &addrResult);
315 if (err)
316 return err;
317 if (addrResult == NULL)
318 return EAI_NONAME;
319 memcpy(s, addrResult->ai_addr, addrResult->ai_addrlen);
320 if (name) {
321 if (addrResult->ai_canonname)
322 *name = xstrdup(addrResult->ai_canonname);
323 else
324 *name = xstrdup(host);
326 freeaddrinfo(addrResult);
327 return 0;
330 #ifdef HAVE_IPV6
331 int is_numeric_ipv6(const char *p)
333 /* A numeric IPv6 address consist at least of 2 ':' and
334 * it may have sequences of hex-digits and maybe contain
335 * a '.' from a IPv4 mapped address and maybe is enclosed in []
336 * we do not check here, if it is a valid IPv6 address
337 * only if is something like a numeric IPv6 address or something else
339 int colon = 0;
340 int dot = 0;
341 int bracket = 0;
342 char c;
344 if (!p)
345 return 0;
347 if (*p == '[') {
348 bracket = 1;
349 p++;
352 while ((c = *p++) && c != ']') {
353 switch (c) {
354 case ':':
355 colon++;
356 break;
357 case '.':
358 dot++;
359 break;
360 case '0': case '1': case '2': case '3': case '4':
361 case '5': case '6': case '7': case '8': case '9':
362 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
363 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
364 break;
365 default:
366 return 0; /* Invalid character */
370 if (colon < 2 || colon > 7)
371 return 0;
373 if (dot) {
374 /* An IPv4-mapped address in dot-quad form will have 3 dots */
375 if (dot != 3)
376 return 0;
377 /* The IPv4-mapped address takes the space of one colon */
378 if (colon > 6)
379 return 0;
382 /* If bracketed, must be closed, and vice versa */
383 if (bracket ^ (c == ']'))
384 return 0;
386 /* Otherwise, assume we're okay */
387 return 1;
390 /* strip [] from numeric IPv6 addreses */
392 char *strip_address(char *addr)
394 char *p;
396 if (is_numeric_ipv6(addr) && (*addr == '[')) {
397 p = addr + strlen(addr);
398 p--;
399 if (*p == ']') {
400 *p = 0;
401 addr++;
404 return addr;
406 #endif