LDAP: internal rename of attr_count
[exim.git] / src / src / ip.c
CommitLineData
059ec3d9
PH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
3386088d 5/* Copyright (c) University of Cambridge 1995 - 2015 */
059ec3d9
PH
6/* See the file NOTICE for conditions of use and distribution. */
7
8/* Functions for doing things with sockets. With the advent of IPv6 this has
9got messier, so that it's worth pulling out the code into separate functions
10that other parts of Exim can call, expecially as there are now several
11different places in the code where sockets are used. */
12
13
14#include "exim.h"
15
16
17/*************************************************
18* Create a socket *
19*************************************************/
20
21/* Socket creation happens in a number of places so it's packaged here for
22convenience.
23
24Arguments:
25 type SOCK_DGRAM or SOCK_STREAM
26 af AF_INET or AF_INET6
27
28Returns: socket number or -1 on failure
29*/
30
31int
32ip_socket(int type, int af)
33{
34int sock = socket(af, type, 0);
35if (sock < 0)
36 log_write(0, LOG_MAIN, "IPv%c socket creation failed: %s",
37 (af == AF_INET6)? '6':'4', strerror(errno));
38return sock;
39}
40
41
42
43
44#if HAVE_IPV6
45/*************************************************
46* Convert printing address to numeric *
47*************************************************/
48
49/* This function converts the textual form of an IP address into a numeric form
50in an appropriate structure in an IPv6 environment. The getaddrinfo() function
51can (apparently) handle more complicated addresses (e.g. those containing
52scopes) than inet_pton() in some environments. We use hints to tell it that the
53input must be a numeric address.
54
55However, apparently some operating systems (or libraries) don't support
56getaddrinfo(), so there is a build-time option to revert to inet_pton() (which
57does not support scopes).
58
59Arguments:
60 address textual form of the address
61 addr where to copy back the answer
62
63Returns: nothing - failure provokes a panic-die
64*/
65
66static void
a56cc2b8 67ip_addrinfo(const uschar *address, struct sockaddr_in6 *saddr)
059ec3d9
PH
68{
69#ifdef IPV6_USE_INET_PTON
70
a56cc2b8 71 if (inet_pton(AF_INET6, CCS address, &saddr->sin6_addr) != 1)
059ec3d9
PH
72 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to parse \"%s\" as an "
73 "IP address", address);
74 saddr->sin6_family = AF_INET6;
75
76#else
77
78 int rc;
79 struct addrinfo hints, *res;
80 memset(&hints, 0, sizeof(hints));
81 hints.ai_family = AF_INET6;
82 hints.ai_socktype = SOCK_STREAM;
83 hints.ai_flags = AI_NUMERICHOST;
a56cc2b8 84 if ((rc = getaddrinfo(CCS address, NULL, &hints, &res)) != 0 || res == NULL)
059ec3d9
PH
85 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to parse \"%s\" as an "
86 "IP address: %s", address,
87 (rc == 0)? "NULL result returned" : gai_strerror(rc));
88 memcpy(saddr, res->ai_addr, res->ai_addrlen);
89 freeaddrinfo(res);
90
91#endif
92}
93#endif /* HAVE_IPV6 */
94
95
96/*************************************************
97* Bind socket to interface and port *
98*************************************************/
99
059ec3d9 100int
7eb6c37c 101ip_addr(void * sin_, int af, const uschar * address, int port)
059ec3d9 102{
7eb6c37c 103union sockaddr_46 * sin = sin_;
69cbeaec 104memset(sin, 0, sizeof(*sin));
059ec3d9
PH
105
106/* Setup code when using an IPv6 socket. The wildcard address is ":", to
107ensure an IPv6 socket is used. */
108
109#if HAVE_IPV6
110if (af == AF_INET6)
111 {
112 if (address[0] == ':' && address[1] == 0)
113 {
7eb6c37c
JH
114 sin->v6.sin6_family = AF_INET6;
115 sin->v6.sin6_addr = in6addr_any;
059ec3d9
PH
116 }
117 else
7eb6c37c
JH
118 ip_addrinfo(address, &sin->v6); /* Panic-dies on error */
119 sin->v6.sin6_port = htons(port);
120 return sizeof(sin->v6);
059ec3d9
PH
121 }
122else
123#else /* HAVE_IPv6 */
124af = af; /* Avoid compiler warning */
125#endif /* HAVE_IPV6 */
126
127/* Setup code when using IPv4 socket. The wildcard address is "". */
128
129 {
7eb6c37c
JH
130 sin->v4.sin_family = AF_INET;
131 sin->v4.sin_port = htons(port);
132 sin->v4.sin_addr.s_addr = address[0] == 0
133 ? (S_ADDR_TYPE)INADDR_ANY
134 : (S_ADDR_TYPE)inet_addr(CS address);
135 return sizeof(sin->v4);
059ec3d9 136 }
7eb6c37c 137}
059ec3d9 138
059ec3d9 139
7eb6c37c
JH
140
141/* This function binds a socket to a local interface address and port. For a
142wildcard IPv6 bind, the address is ":".
143
144Arguments:
145 sock the socket
146 af AF_INET or AF_INET6 - the socket type
147 address the IP address, in text form
148 port the IP port (host order)
149
150Returns: the result of bind()
151*/
152
153int
154ip_bind(int sock, int af, uschar *address, int port)
155{
156union sockaddr_46 sin;
157int s_len = ip_addr(&sin, af, address, port);
059ec3d9
PH
158return bind(sock, (struct sockaddr *)&sin, s_len);
159}
160
161
162
163/*************************************************
164* Connect socket to remote host *
165*************************************************/
166
167/* This function connects a socket to a remote address and port. The socket may
d515a917
PH
168or may not have previously been bound to a local interface. The socket is not
169closed, even in cases of error. It is expected that the calling function, which
170created the socket, will be the one that closes it.
059ec3d9
PH
171
172Arguments:
173 sock the socket
174 af AF_INET6 or AF_INET for the socket type
175 address the remote address, in text form
176 port the remote port
b1f8e4f8 177 timeout a timeout (zero for indefinite timeout)
059ec3d9
PH
178
179Returns: 0 on success; -1 on failure, with errno set
180*/
181
182int
a56cc2b8 183ip_connect(int sock, int af, const uschar *address, int port, int timeout)
059ec3d9
PH
184{
185struct sockaddr_in s_in4;
186struct sockaddr *s_ptr;
187int s_len, rc, save_errno;
188
189/* For an IPv6 address, use an IPv6 sockaddr structure. */
190
191#if HAVE_IPV6
192struct sockaddr_in6 s_in6;
193if (af == AF_INET6)
194 {
195 memset(&s_in6, 0, sizeof(s_in6));
196 ip_addrinfo(address, &s_in6); /* Panic-dies on error */
197 s_in6.sin6_port = htons(port);
198 s_ptr = (struct sockaddr *)&s_in6;
199 s_len = sizeof(s_in6);
200 }
201else
202#else /* HAVE_IPV6 */
203af = af; /* Avoid compiler warning */
204#endif /* HAVE_IPV6 */
205
206/* For an IPv4 address, use an IPv4 sockaddr structure, even on a system with
207IPv6 support. */
208
209 {
210 memset(&s_in4, 0, sizeof(s_in4));
211 s_in4.sin_family = AF_INET;
212 s_in4.sin_port = htons(port);
a56cc2b8 213 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(CCS address);
059ec3d9
PH
214 s_ptr = (struct sockaddr *)&s_in4;
215 s_len = sizeof(s_in4);
216 }
217
218/* If no connection timeout is set, just call connect() without setting a
219timer, thereby allowing the inbuilt OS timeout to operate. */
220
221sigalrm_seen = FALSE;
222if (timeout > 0) alarm(timeout);
223rc = connect(sock, s_ptr, s_len);
224save_errno = errno;
225alarm(0);
226
227/* There is a testing facility for simulating a connection timeout, as I
228can't think of any other way of doing this. It converts a connection refused
75e0e026 229into a timeout if the timeout is set to 999999. */
059ec3d9 230
a39bd74d 231if (running_in_test_harness && save_errno == ECONNREFUSED && timeout == 999999)
059ec3d9 232 {
a39bd74d
JB
233 rc = -1;
234 save_errno = EINTR;
235 sigalrm_seen = TRUE;
059ec3d9
PH
236 }
237
238/* Success */
239
055e2cb4
JH
240if (rc >= 0)
241 {
242 callout_address = string_sprintf("[%s]:%d", address, port);
243 return 0;
244 }
059ec3d9
PH
245
246/* A failure whose error code is "Interrupted system call" is in fact
247an externally applied timeout if the signal handler has been run. */
248
a39bd74d 249errno = save_errno == EINTR && sigalrm_seen ? ETIMEDOUT : save_errno;
059ec3d9
PH
250return -1;
251}
252
253
a6d4c44e
TF
254
255/*************************************************
256* Create connected socket to remote host *
257*************************************************/
258
b1f8e4f8
JH
259/* Create a socket and connect to host (name or number, ipv6 ok)
260 at one of port-range.
a6d4c44e 261
b1f8e4f8
JH
262Arguments:
263 type SOCK_DGRAM or SOCK_STREAM
264 af AF_INET6 or AF_INET for the socket type
265 address the remote address, in text form
266 portlo,porthi the remote port range
267 timeout a timeout
268 connhost if not NULL, host_item filled in with connection details
269 errstr pointer for allocated string on error
270
271Return:
272 socket fd, or -1 on failure (having allocated an error string)
273*/
274int
275ip_connectedsocket(int type, const uschar * hostname, int portlo, int porthi,
276 int timeout, host_item * connhost, uschar ** errstr)
277{
278int namelen, port;
279host_item shost;
280host_item *h;
281int af = 0, fd, fd4 = -1, fd6 = -1;
282
283shost.next = NULL;
284shost.address = NULL;
285shost.port = portlo;
286shost.mx = -1;
287
288namelen = Ustrlen(hostname);
289
290/* Anything enclosed in [] must be an IP address. */
291
292if (hostname[0] == '[' &&
293 hostname[namelen - 1] == ']')
294 {
295 uschar * host = string_copy(hostname);
296 host[namelen - 1] = 0;
297 host++;
298 if (string_is_ip_address(host, NULL) == 0)
299 {
300 *errstr = string_sprintf("malformed IP address \"%s\"", hostname);
301 return -1;
302 }
303 shost.name = shost.address = host;
304 }
305
306/* Otherwise check for an unadorned IP address */
307
308else if (string_is_ip_address(hostname, NULL) != 0)
309 shost.name = shost.address = string_copy(hostname);
310
311/* Otherwise lookup IP address(es) from the name */
312
313else
314 {
315 shost.name = string_copy(hostname);
1f155f8e
JH
316 if (host_find_byname(&shost, NULL, HOST_FIND_QUALIFY_SINGLE,
317 NULL, FALSE) != HOST_FOUND)
b1f8e4f8
JH
318 {
319 *errstr = string_sprintf("no IP address found for host %s", shost.name);
320 return -1;
321 }
322 }
323
324/* Try to connect to the server - test each IP till one works */
325
326for (h = &shost; h != NULL; h = h->next)
327 {
328 fd = (Ustrchr(h->address, ':') != 0)
a6d4c44e
TF
329 ? (fd6 < 0) ? (fd6 = ip_socket(type, af = AF_INET6)) : fd6
330 : (fd4 < 0) ? (fd4 = ip_socket(type, af = AF_INET )) : fd4;
b1f8e4f8
JH
331
332 if (fd < 0)
333 {
334 *errstr = string_sprintf("failed to create socket: %s", strerror(errno));
335 goto bad;
336 }
337
338 for(port = portlo; port <= porthi; port++)
339 if (ip_connect(fd, af, h->address, port, timeout) == 0)
340 {
341 if (fd != fd6) close(fd6);
342 if (fd != fd4) close(fd4);
8a512ed5
JH
343 if (connhost)
344 {
b1f8e4f8
JH
345 h->port = port;
346 *connhost = *h;
347 connhost->next = NULL;
348 }
349 return fd;
350 }
351 }
352
a9764ac5
JH
353*errstr = string_sprintf("failed to connect to any address for %s: %s",
354 hostname, strerror(errno));
b1f8e4f8
JH
355
356bad:
357 close(fd4); close(fd6); return -1;
358}
359
059ec3d9 360
3e60dd41
JH
361int
362ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo)
363{
a39bd74d
JB
364int scan;
365uschar hostname[256];
366unsigned int portlow, porthigh;
367
368/* extract host and port part */
369scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh);
370if (scan != 3)
371 {
372 if (scan != 2)
373 {
374 *errstr = string_sprintf("invalid socket '%s'", hostport);
375 return -1;
3e60dd41 376 }
a39bd74d 377 porthigh = portlow;
3e60dd41
JH
378 }
379
a39bd74d
JB
380return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh,
381 tmo, NULL, errstr);
3e60dd41
JH
382}
383
384int
385ip_unixsocket(const uschar * path, uschar ** errstr)
386{
a39bd74d
JB
387int sock;
388struct sockaddr_un server;
3e60dd41 389
a39bd74d
JB
390if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
391 {
392 *errstr = US"can't open UNIX socket.";
393 return -1;
3e60dd41
JH
394 }
395
a39bd74d
JB
396server.sun_family = AF_UNIX;
397Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1);
398server.sun_path[sizeof(server.sun_path)-1] = '\0';
399if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0)
400 {
401 int err = errno;
402 (void)close(sock);
403 *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s",
404 path, strerror(err));
405 return -1;
406 }
055e2cb4 407callout_address = string_copy(path);
a39bd74d 408return sock;
3e60dd41
JH
409}
410
411int
412ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo)
413{
a39bd74d
JB
414return *spec == '/'
415 ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo);
3e60dd41
JH
416}
417
059ec3d9
PH
418/*************************************************
419* Set keepalive on a socket *
420*************************************************/
421
422/* Can be called for both incoming and outgoing sockets.
423
424Arguments:
425 sock the socket
426 address the remote host address, for failure logging
427 torf true for outgoing connection, false for incoming
428
429Returns: nothing
430*/
431
432void
55414b25 433ip_keepalive(int sock, const uschar *address, BOOL torf)
059ec3d9
PH
434{
435int fodder = 1;
436if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
437 (uschar *)(&fodder), sizeof(fodder)) != 0)
438 log_write(0, LOG_MAIN, "setsockopt(SO_KEEPALIVE) on connection %s %s "
439 "failed: %s", torf? "to":"from", address, strerror(errno));
440}
441
442
443
444/*************************************************
445* Receive from a socket with timeout *
446*************************************************/
447
4e71661f 448/*
059ec3d9 449Arguments:
4e71661f
JH
450 fd the file descriptor
451 timeout the timeout, seconds
452Returns: TRUE => ready for i/o
453 FALSE => timed out, or other error
059ec3d9 454*/
4e71661f
JH
455BOOL
456fd_ready(int fd, int timeout)
059ec3d9
PH
457{
458fd_set select_inset;
19050083 459time_t start_recv = time(NULL);
c528cec4 460int time_left = timeout;
059ec3d9
PH
461int rc;
462
85ff3cf9 463if (time_left <= 0)
4e71661f
JH
464 {
465 errno = ETIMEDOUT;
466 return FALSE;
467 }
059ec3d9
PH
468/* Wait until the socket is ready */
469
a39bd74d 470do
059ec3d9 471 {
c528cec4 472 struct timeval tv = { time_left, 0 };
059ec3d9 473 FD_ZERO (&select_inset);
4e71661f 474 FD_SET (fd, &select_inset);
059ec3d9 475
0f0c8159 476 /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/
4e71661f 477 rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv);
059ec3d9
PH
478
479 /* If some interrupt arrived, just retry. We presume this to be rare,
480 but it can happen (e.g. the SIGUSR1 signal sent by exiwhat causes
481 select() to exit).
482
483 Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making
484 the interrupt not at all rare. Since the timeout is typically more than 2
485 minutes, the effect was to block the timeout completely. To prevent this
c528cec4
HSHR
486 happening again, we do an explicit time test and adjust the timeout
487 accordingly */
059ec3d9
PH
488
489 if (rc < 0 && errno == EINTR)
490 {
491 DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
85ff3cf9 492
c528cec4 493 /* Watch out, 'continue' jumps to the condition, not to the loops top */
85ff3cf9
HSHR
494 time_left = timeout - (time(NULL) - start_recv);
495 if (time_left > 0) continue;
059ec3d9
PH
496 }
497
059ec3d9
PH
498 if (rc <= 0)
499 {
500 errno = ETIMEDOUT;
4e71661f 501 return FALSE;
059ec3d9
PH
502 }
503
c528cec4
HSHR
504 /* Checking the FD_ISSET is not enough, if we're interrupted, the
505 select_inset may still contain the 'input'. */
059ec3d9 506 }
4bd6107d 507while (rc < 0 || !FD_ISSET(fd, &select_inset));
4e71661f
JH
508return TRUE;
509}
510
511/* The timeout is implemented using select(), and we loop to cover select()
512getting interrupted, and the possibility of select() returning with a positive
513result but no ready descriptor. Is this in fact possible?
514
515Arguments:
516 sock the socket
517 buffer to read into
518 bufsize the buffer size
519 timeout the timeout
520
521Returns: > 0 => that much data read
522 <= 0 on error or EOF; errno set - zero for EOF
523*/
524
525int
526ip_recv(int sock, uschar *buffer, int buffsize, int timeout)
527{
528int rc;
529
530if (!fd_ready(sock, timeout))
531 return -1;
059ec3d9
PH
532
533/* The socket is ready, read from it (via TLS if it's active). On EOF (i.e.
534close down of the connection), set errno to zero; otherwise leave it alone. */
535
536#ifdef SUPPORT_TLS
817d9f57
JH
537if (tls_out.active == sock)
538 rc = tls_read(FALSE, buffer, buffsize);
539else if (tls_in.active == sock)
540 rc = tls_read(TRUE, buffer, buffsize);
059ec3d9
PH
541else
542#endif
543 rc = recv(sock, buffer, buffsize, 0);
544
545if (rc > 0) return rc;
546if (rc == 0) errno = 0;
547return -1;
548}
549
550
9e4f5962
PP
551
552
13363eba
PP
553/*************************************************
554* Lookup address family of potential socket *
555*************************************************/
556
557/* Given a file-descriptor, check to see if it's a socket and, if so,
558return the address family; detects IPv4 vs IPv6. If not a socket then
559return -1.
560
561The value 0 is typically AF_UNSPEC, which should not be seen on a connected
562fd. If the return is -1, the errno will be from getsockname(); probably
563ENOTSOCK or ECONNRESET.
564
565Arguments: socket-or-not fd
566Returns: address family or -1
567*/
568
569int
570ip_get_address_family(int fd)
571{
572struct sockaddr_storage ss;
573socklen_t sslen = sizeof(ss);
574
575if (getsockname(fd, (struct sockaddr *) &ss, &sslen) < 0)
576 return -1;
577
578return (int) ss.ss_family;
579}
580
581
582
583
9e4f5962
PP
584/*************************************************
585* Lookup DSCP settings for a socket *
586*************************************************/
587
588struct dscp_name_tableentry {
589 const uschar *name;
590 int value;
591};
592/* Keep both of these tables sorted! */
593static struct dscp_name_tableentry dscp_table[] = {
594#ifdef IPTOS_DSCP_AF11
36a3ae5f
PP
595 { CUS"af11", IPTOS_DSCP_AF11 },
596 { CUS"af12", IPTOS_DSCP_AF12 },
597 { CUS"af13", IPTOS_DSCP_AF13 },
598 { CUS"af21", IPTOS_DSCP_AF21 },
599 { CUS"af22", IPTOS_DSCP_AF22 },
600 { CUS"af23", IPTOS_DSCP_AF23 },
601 { CUS"af31", IPTOS_DSCP_AF31 },
602 { CUS"af32", IPTOS_DSCP_AF32 },
603 { CUS"af33", IPTOS_DSCP_AF33 },
604 { CUS"af41", IPTOS_DSCP_AF41 },
605 { CUS"af42", IPTOS_DSCP_AF42 },
606 { CUS"af43", IPTOS_DSCP_AF43 },
607 { CUS"ef", IPTOS_DSCP_EF },
9e4f5962
PP
608#endif
609#ifdef IPTOS_LOWCOST
36a3ae5f 610 { CUS"lowcost", IPTOS_LOWCOST },
9e4f5962 611#endif
36a3ae5f 612 { CUS"lowdelay", IPTOS_LOWDELAY },
9e4f5962 613#ifdef IPTOS_MINCOST
36a3ae5f 614 { CUS"mincost", IPTOS_MINCOST },
9e4f5962 615#endif
36a3ae5f
PP
616 { CUS"reliability", IPTOS_RELIABILITY },
617 { CUS"throughput", IPTOS_THROUGHPUT }
9e4f5962
PP
618};
619static int dscp_table_size =
620 sizeof(dscp_table) / sizeof(struct dscp_name_tableentry);
621
622/* DSCP values change by protocol family, and so do the options used for
2a1b36b3
PP
623setsockopt(); this utility does all the lookups. It takes an unexpanded
624option string, expands it, strips off affix whitespace, then checks if it's
625a number. If all of what's left is a number, then that's how the option will
626be parsed and success/failure is a range check. If it's not all a number,
627then it must be a supported keyword.
9e4f5962
PP
628
629Arguments:
630 dscp_name a string, so far unvalidated
631 af address_family in use
632 level setsockopt level to use
633 optname setsockopt name to use
634 dscp_value value for dscp_name
635
636Returns: TRUE if okay to setsockopt(), else FALSE
2a1b36b3
PP
637
638*level and *optname may be set even if FALSE is returned
9e4f5962
PP
639*/
640
641BOOL
642dscp_lookup(const uschar *dscp_name, int af,
643 int *level, int *optname, int *dscp_value)
644{
2a1b36b3 645uschar *dscp_lookup, *p;
9e4f5962 646int first, last;
2a1b36b3 647long rawlong;
9e4f5962
PP
648
649if (af == AF_INET)
650 {
651 *level = IPPROTO_IP;
652 *optname = IP_TOS;
653 }
bb7b9411 654#if HAVE_IPV6 && defined(IPV6_TCLASS)
9e4f5962
PP
655else if (af == AF_INET6)
656 {
657 *level = IPPROTO_IPV6;
658 *optname = IPV6_TCLASS;
659 }
b301a50b 660#endif
9e4f5962
PP
661else
662 {
663 DEBUG(D_transport)
664 debug_printf("Unhandled address family %d in dscp_lookup()\n", af);
665 return FALSE;
666 }
667if (!dscp_name)
668 {
669 DEBUG(D_transport)
670 debug_printf("[empty DSCP]\n");
671 return FALSE;
672 }
673dscp_lookup = expand_string(US dscp_name);
674if (dscp_lookup == NULL || *dscp_lookup == '\0')
675 return FALSE;
676
2a1b36b3
PP
677p = dscp_lookup + Ustrlen(dscp_lookup) - 1;
678while (isspace(*p)) *p-- = '\0';
679while (isspace(*dscp_lookup) && dscp_lookup < p) dscp_lookup++;
680if (*dscp_lookup == '\0')
681 return FALSE;
682
683rawlong = Ustrtol(dscp_lookup, &p, 0);
684if (p != dscp_lookup && *p == '\0')
685 {
686 /* We have six bits available, which will end up shifted to fit in 0xFC mask.
687 RFC 2597 defines the values unshifted. */
688 if (rawlong < 0 || rawlong > 0x3F)
689 {
690 DEBUG(D_transport)
691 debug_printf("DSCP value %ld out of range, ignored.\n", rawlong);
692 return FALSE;
693 }
694 *dscp_value = rawlong << 2;
695 return TRUE;
696 }
697
9e4f5962
PP
698first = 0;
699last = dscp_table_size;
700while (last > first)
701 {
702 int middle = (first + last)/2;
703 int c = Ustrcmp(dscp_lookup, dscp_table[middle].name);
704 if (c == 0)
705 {
706 *dscp_value = dscp_table[middle].value;
707 return TRUE;
708 }
709 else if (c > 0)
9e4f5962 710 first = middle + 1;
9e4f5962 711 else
9e4f5962 712 last = middle;
9e4f5962
PP
713 }
714return FALSE;
715}
716
36a3ae5f
PP
717void
718dscp_list_to_stream(FILE *stream)
719{
720int i;
721for (i=0; i < dscp_table_size; ++i)
722 fprintf(stream, "%s\n", dscp_table[i].name);
723}
724
9e4f5962 725
059ec3d9 726/* End of ip.c */
8a512ed5
JH
727/* vi: aw ai sw=2
728*/