diff options
Diffstat (limited to 'src/gns/gns_util.c')
-rw-r--r-- | src/gns/gns_util.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/src/gns/gns_util.c b/src/gns/gns_util.c new file mode 100644 index 000000000..b2f5e1f38 --- /dev/null +++ b/src/gns/gns_util.c | |||
@@ -0,0 +1,196 @@ | |||
1 | #include "gns.h" | ||
2 | |||
3 | /** | ||
4 | * Add a DNS name to the buffer at the given location. | ||
5 | * | ||
6 | * @param dst where to write the name | ||
7 | * @param dst_len number of bytes in dst | ||
8 | * @param off pointer to offset where to write the name (increment by bytes used) | ||
9 | * must not be changed if there is an error | ||
10 | * @param name name to write | ||
11 | * @return GNUNET_SYSERR if 'name' is invalid | ||
12 | * GNUNET_NO if 'name' did not fit | ||
13 | * GNUNET_OK if 'name' was added to 'dst' | ||
14 | */ | ||
15 | static int | ||
16 | add_name (char *dst, | ||
17 | size_t dst_len, | ||
18 | size_t *off, | ||
19 | const char *name) | ||
20 | { | ||
21 | const char *dot; | ||
22 | size_t start; | ||
23 | size_t pos; | ||
24 | size_t len; | ||
25 | |||
26 | if (NULL == name) | ||
27 | return GNUNET_SYSERR; | ||
28 | start = *off; | ||
29 | if (start + strlen (name) + 2 > dst_len) | ||
30 | return GNUNET_NO; | ||
31 | pos = start; | ||
32 | do | ||
33 | { | ||
34 | dot = strchr (name, '.'); | ||
35 | if (NULL == dot) | ||
36 | len = strlen (name); | ||
37 | else | ||
38 | len = dot - name; | ||
39 | if ( (len >= 64) || (len == 0) ) | ||
40 | return GNUNET_NO; /* segment too long or empty */ | ||
41 | dst[pos++] = (char) (uint8_t) len; | ||
42 | memcpy (&dst[pos], name, len); | ||
43 | pos += len; | ||
44 | name += len + 1; /* also skip dot */ | ||
45 | } | ||
46 | while (NULL != dot); | ||
47 | dst[pos++] = '\0'; /* terminator */ | ||
48 | *off = pos; | ||
49 | return GNUNET_OK; | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * Add an MX record to the buffer at the given location. | ||
54 | * | ||
55 | * @param dst where to write the mx record | ||
56 | * @param dst_len number of bytes in dst | ||
57 | * @param off pointer to offset where to write the mx information (increment by bytes used); | ||
58 | * can also change if there was an error | ||
59 | * @param mx mx information to write | ||
60 | * @return GNUNET_SYSERR if 'mx' is invalid | ||
61 | * GNUNET_NO if 'mx' did not fit | ||
62 | * GNUNET_OK if 'mx' was added to 'dst' | ||
63 | */ | ||
64 | static int | ||
65 | add_mx (char *dst, | ||
66 | size_t dst_len, | ||
67 | size_t *off, | ||
68 | const struct GNUNET_DNSPARSER_MxRecord *mx) | ||
69 | { | ||
70 | uint16_t mxpref; | ||
71 | |||
72 | if (*off + sizeof (uint16_t) > dst_len) | ||
73 | return GNUNET_NO; | ||
74 | mxpref = htons (mx->preference); | ||
75 | memcpy (&dst[*off], &mxpref, sizeof (mxpref)); | ||
76 | (*off) += sizeof (mxpref); | ||
77 | return add_name (dst, dst_len, off, mx->mxhost); | ||
78 | } | ||
79 | |||
80 | |||
81 | /** | ||
82 | * Add an SOA record to the buffer at the given location. | ||
83 | * | ||
84 | * @param dst where to write the SOA record | ||
85 | * @param dst_len number of bytes in dst | ||
86 | * @param off pointer to offset where to write the SOA information (increment by bytes used) | ||
87 | * can also change if there was an error | ||
88 | * @param soa SOA information to write | ||
89 | * @return GNUNET_SYSERR if 'soa' is invalid | ||
90 | * GNUNET_NO if 'soa' did not fit | ||
91 | * GNUNET_OK if 'soa' was added to 'dst' | ||
92 | */ | ||
93 | static int | ||
94 | add_soa (char *dst, | ||
95 | size_t dst_len, | ||
96 | size_t *off, | ||
97 | const struct GNUNET_DNSPARSER_SoaRecord *soa) | ||
98 | { | ||
99 | struct soa_data sd; | ||
100 | int ret; | ||
101 | |||
102 | if ( (GNUNET_OK != (ret = add_name (dst, | ||
103 | dst_len, | ||
104 | off, | ||
105 | soa->mname))) || | ||
106 | (GNUNET_OK != (ret = add_name (dst, | ||
107 | dst_len, | ||
108 | off, | ||
109 | soa->rname)) ) ) | ||
110 | return ret; | ||
111 | if (*off + sizeof (struct soa_data) > dst_len) | ||
112 | return GNUNET_NO; | ||
113 | sd.serial = htonl (soa->serial); | ||
114 | sd.refresh = htonl (soa->refresh); | ||
115 | sd.retry = htonl (soa->retry); | ||
116 | sd.expire = htonl (soa->expire); | ||
117 | sd.minimum = htonl (soa->minimum_ttl); | ||
118 | memcpy (&dst[*off], &sd, sizeof (sd)); | ||
119 | (*off) += sizeof (sd); | ||
120 | return GNUNET_OK; | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Add a DNS record to the buffer at the given location. | ||
125 | * | ||
126 | * @param dst where to write the record | ||
127 | * @param dst_len number of bytes in dst | ||
128 | * @param off pointer to offset where to write the query (increment by bytes used) | ||
129 | * must not be changed if there is an error | ||
130 | * @param record record to write | ||
131 | * @return GNUNET_SYSERR if 'record' is invalid | ||
132 | * GNUNET_NO if 'record' did not fit | ||
133 | * GNUNET_OK if 'record' was added to 'dst' | ||
134 | */ | ||
135 | static int | ||
136 | parse_record (char *dst, | ||
137 | size_t dst_len, | ||
138 | size_t *off, | ||
139 | const struct GNUNET_GNS_Record *record) | ||
140 | { | ||
141 | int ret; | ||
142 | size_t start; | ||
143 | size_t pos; | ||
144 | struct record_line rl; | ||
145 | |||
146 | start = *off; | ||
147 | ret = add_name (dst, dst_len - sizeof (struct record_line), off, record->name); | ||
148 | if (ret != GNUNET_OK) | ||
149 | return ret; | ||
150 | /* '*off' is now the position where we will need to write the record line */ | ||
151 | |||
152 | pos = *off + sizeof (struct record_line); | ||
153 | switch (record->type) | ||
154 | { | ||
155 | case GNUNET_DNSPARSER_TYPE_MX: | ||
156 | ret = add_mx (dst, dst_len, &pos, record->data.mx); | ||
157 | break; | ||
158 | case GNUNET_DNSPARSER_TYPE_SOA: | ||
159 | ret = add_soa (dst, dst_len, &pos, record->data.soa); | ||
160 | break; | ||
161 | case GNUNET_DNSPARSER_TYPE_NS: | ||
162 | case GNUNET_DNSPARSER_TYPE_CNAME: | ||
163 | case GNUNET_DNSPARSER_TYPE_PTR: | ||
164 | ret = add_name (dst, dst_len, &pos, record->data.hostname); | ||
165 | break; | ||
166 | default: | ||
167 | if (pos + record->data.raw.data_len > dst_len) | ||
168 | { | ||
169 | ret = GNUNET_NO; | ||
170 | break; | ||
171 | } | ||
172 | memcpy (&dst[pos], record->data.raw.data, record->data.raw.data_len); | ||
173 | pos += record->data.raw.data_len; | ||
174 | ret = GNUNET_OK; | ||
175 | break; | ||
176 | } | ||
177 | if (ret != GNUNET_OK) | ||
178 | { | ||
179 | *off = start; | ||
180 | return GNUNET_NO; | ||
181 | } | ||
182 | |||
183 | if (pos - (*off + sizeof (struct record_line)) > UINT16_MAX) | ||
184 | { | ||
185 | /* record data too long */ | ||
186 | *off = start; | ||
187 | return GNUNET_NO; | ||
188 | } | ||
189 | rl.type = htons (record->type); | ||
190 | rl.class = htons (record->class); | ||
191 | rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value / 1000); /* in seconds */ | ||
192 | rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct record_line)))); | ||
193 | memcpy (&dst[*off], &rl, sizeof (struct record_line)); | ||
194 | *off = pos; | ||
195 | return GNUNET_OK; | ||
196 | } | ||