Coverage Report

Created: 2026-02-26 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntp-dev/libntp/decodenetnum.c
Line
Count
Source
1
/*
2
 * decodenetnum - return a net number (this is crude, but careful)
3
 */
4
#include <config.h>
5
#include <sys/types.h>
6
#include <ctype.h>
7
#ifdef HAVE_SYS_SOCKET_H
8
#include <sys/socket.h>
9
#endif
10
#ifdef HAVE_NETINET_IN_H
11
#include <netinet/in.h>
12
#endif
13
14
#include "ntp.h"
15
#include "ntp_stdlib.h"
16
17
18
/* If the given string position points to a decimal digit, parse the
19
 * number. If this is not possible, or the parsing did not consume the
20
 * whole string, or if the result exceeds the maximum value, return the
21
 * default value.
22
 */
23
static unsigned long
24
_num_or_dflt(
25
  char *    sval,
26
  unsigned long maxval,
27
  unsigned long defval
28
  )
29
0
{
30
0
  char *    ep;
31
0
  unsigned long num;
32
  
33
0
  if (!(sval && isdigit(*(unsigned char*)sval)))
34
0
    return defval;
35
  
36
0
  num = strtoul(sval, &ep, 10);
37
0
  if (!*ep && num <= maxval)
38
0
    return num;
39
  
40
0
  return defval;
41
0
}
42
43
/* If the given string position is not NULL and does not point to the
44
 * terminator, replace the character with NUL and advance the pointer.
45
 * Return the resulting position.
46
 */
47
static inline char*
48
_chop(
49
  char * sp)
50
0
{
51
0
  if (sp && *sp)
52
0
    *sp++ = '\0';
53
0
  return sp;
54
0
}
55
56
/* If the given string position points to the given char, advance the
57
 * pointer and return the result. Otherwise, return NULL.
58
 */
59
static inline char*
60
_skip(
61
  char * sp,
62
  int    ch)
63
0
{
64
0
  if (sp && *(unsigned char*)sp == ch)
65
0
    return (sp + 1);
66
0
  return NULL;
67
0
}
68
69
/*
70
 * decodenetnum   convert text IP address and port to sockaddr_u
71
 *
72
 * Returns FALSE (->0) for failure, TRUE (->1) for success.
73
 */
74
int
75
decodenetnum(
76
  const char *num,
77
  sockaddr_u *net
78
  )
79
0
{
80
  /* Building a parser is more fun in Haskell, but here we go...
81
   *
82
   * This works through 'inet_pton()' taking the brunt of the
83
   * work, after some string manipulations to split off URI
84
   * brackets, ports and scope identifiers. The heuristics are
85
   * simple but must hold for all _VALID_ addresses. inet_pton()
86
   * will croak on bad ones later, but replicating the whole
87
   * parser logic to detect errors is wasteful.
88
   */
89
  
90
0
  sockaddr_u  netnum;
91
0
  char    buf[64];  /* working copy of input */
92
0
  char    *haddr=buf;
93
0
  unsigned int  port=NTP_PORT, scope=0;
94
0
  unsigned short  afam=AF_UNSPEC;
95
  
96
  /* copy input to working buffer with length check */
97
0
  if (strlcpy(buf, num, sizeof(buf)) >= sizeof(buf))
98
0
    return FALSE;
99
100
  /* Identify address family and possibly the port, if given.  If
101
   * this results in AF_UNSPEC, we will fail in the next step.
102
   */
103
0
  if (*haddr == '[') {
104
0
    char * endp = strchr(++haddr, ']');
105
0
    if (endp) {
106
0
      port = _num_or_dflt(_skip(_chop(endp), ':'),
107
0
                0xFFFFu, port);
108
0
      afam = strchr(haddr, ':') ? AF_INET6 : AF_INET;
109
0
    }
110
0
  } else {
111
0
    char *col = strchr(haddr, ':');
112
0
    char *dot = strchr(haddr, '.');
113
0
    if (col == dot) {
114
      /* no dot, no colon: bad! */
115
0
      afam = AF_UNSPEC;
116
0
    } else if (!col) {
117
      /* no colon, only dot: IPv4! */
118
0
      afam = AF_INET;
119
0
    } else if (!dot || col < dot) {
120
      /* no dot or 1st colon before 1st dot: IPv6! */
121
0
      afam = AF_INET6;
122
0
    } else {
123
      /* 1st dot before 1st colon: must be IPv4 with port */
124
0
      afam = AF_INET;
125
0
      port = _num_or_dflt(_chop(col), 0xFFFFu, port);
126
0
    }
127
0
  }
128
129
  /* Since we don't know about additional members in the address
130
   * structures, we wipe the result buffer thoroughly:
131
   */   
132
0
  memset(&netnum, 0, sizeof(netnum));
133
134
  /* For AF_INET6, evaluate and remove any scope suffix. Have
135
   * inet_pton() do the real work for AF_INET and AF_INET6, bail
136
   * out otherwise:
137
   */
138
0
  switch (afam) {
139
0
  case AF_INET:
140
0
    if (inet_pton(afam, haddr, &netnum.sa4.sin_addr) <= 0)
141
0
      return FALSE;
142
0
    netnum.sa4.sin_port = htons((unsigned short)port);
143
0
    break;
144
145
0
  case AF_INET6:
146
0
    scope = _num_or_dflt(_chop(strchr(haddr, '%')), 0xFFFFFFFFu, scope);
147
0
    if (inet_pton(afam, haddr, &netnum.sa6.sin6_addr) <= 0)
148
0
      return FALSE;
149
0
    netnum.sa6.sin6_port = htons((unsigned short)port);
150
0
    netnum.sa6.sin6_scope_id = scope;
151
0
    break;
152
153
0
  case AF_UNSPEC:
154
0
  default:
155
0
    return FALSE;
156
0
  }
157
158
  /* Collect the remaining pieces and feed the output, which was
159
   * not touched so far:
160
   */
161
0
  netnum.sa.sa_family = afam;
162
0
  memcpy(net, &netnum, sizeof(netnum));
163
0
  return TRUE;
164
0
}