Merge branch 'topic/mm-fixes' of git://208.110.73.182/silc into silc.1.1.branch
[silc.git] / lib / silccore / silcid.c
1 /*
2
3   id.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcid.h"
23
24 /* ID lengths (in bytes) without the IP address part */
25 #define ID_SERVER_LEN_PART      4
26 #define ID_CLIENT_LEN_PART      CLIENTID_HASH_LEN + 1
27 #define ID_CHANNEL_LEN_PART     4
28
29 /******************************************************************************
30
31                                 ID Payload
32
33 ******************************************************************************/
34
35 struct SilcIDPayloadStruct {
36   SilcIdType type;
37   SilcUInt16 len;
38   unsigned char *id;
39 };
40
41 /* Parses buffer and return ID payload into payload structure */
42
43 SilcIDPayload silc_id_payload_parse(const unsigned char *payload,
44                                     SilcUInt32 payload_len)
45 {
46   SilcBufferStruct buffer;
47   SilcIDPayload newp;
48   int ret;
49
50   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
51   newp = silc_calloc(1, sizeof(*newp));
52   if (!newp)
53     return NULL;
54
55   ret = silc_buffer_unformat(&buffer,
56                              SILC_STR_UI_SHORT(&newp->type),
57                              SILC_STR_UI_SHORT(&newp->len),
58                              SILC_STR_END);
59   if (ret == -1)
60     goto err;
61
62   if (newp->type > SILC_ID_CHANNEL)
63     goto err;
64
65   silc_buffer_pull(&buffer, 4);
66
67   if (newp->len > silc_buffer_len(&buffer) ||
68       newp->len > SILC_PACKET_MAX_ID_LEN)
69     goto err;
70
71   ret = silc_buffer_unformat(&buffer,
72                              SILC_STR_DATA_ALLOC(&newp->id, newp->len),
73                              SILC_STR_END);
74   if (ret == -1)
75     goto err;
76
77   return newp;
78
79  err:
80   SILC_LOG_DEBUG(("Error parsing ID payload"));
81   silc_free(newp);
82   return NULL;
83 }
84
85 /* Return the ID directly from the raw payload data. */
86
87 SilcBool silc_id_payload_parse_id(const unsigned char *data, SilcUInt32 len,
88                                   SilcID *ret_id)
89 {
90   SilcBufferStruct buffer;
91   SilcIdType type;
92   SilcUInt16 idlen;
93   unsigned char *id_data;
94   int ret;
95
96   if (!ret_id)
97     return FALSE;
98
99   silc_buffer_set(&buffer, (unsigned char *)data, len);
100   ret = silc_buffer_unformat(&buffer,
101                              SILC_STR_UI_SHORT(&type),
102                              SILC_STR_UI_SHORT(&idlen),
103                              SILC_STR_END);
104   if (ret == -1)
105     goto err;
106
107   if (type > SILC_ID_CHANNEL)
108     goto err;
109
110   silc_buffer_pull(&buffer, 4);
111
112   if (idlen > silc_buffer_len(&buffer) || idlen > SILC_PACKET_MAX_ID_LEN)
113     goto err;
114
115   ret = silc_buffer_unformat(&buffer,
116                              SILC_STR_DATA(&id_data, idlen),
117                              SILC_STR_END);
118   if (ret == -1)
119     goto err;
120
121   ret_id->type = type;
122
123   if (type == SILC_ID_CLIENT) {
124     if (!silc_id_str2id(id_data, idlen, type, &ret_id->u.client_id,
125                         sizeof(SilcClientID)))
126       goto err;
127   } else if (type == SILC_ID_SERVER) {
128     if (!silc_id_str2id(id_data, idlen, type, &ret_id->u.server_id,
129                         sizeof(SilcServerID)))
130       goto err;
131   } else {
132     if (!silc_id_str2id(id_data, idlen, type, &ret_id->u.channel_id,
133                         sizeof(SilcChannelID)))
134       goto err;
135   }
136
137   return TRUE;
138
139  err:
140   SILC_LOG_DEBUG(("Error parsing ID payload"));
141   return FALSE;
142 }
143
144 /* Encodes ID Payload */
145
146 SilcBuffer silc_id_payload_encode(const void *id, SilcIdType type)
147 {
148   SilcBuffer buffer;
149   unsigned char id_data[32];
150   SilcUInt32 len;
151
152   if (!silc_id_id2str(id, type, id_data, sizeof(id_data), &len))
153     return NULL;
154   buffer = silc_id_payload_encode_data((const unsigned char *)id_data,
155                                        len, type);
156   return buffer;
157 }
158
159 SilcBuffer silc_id_payload_encode_data(const unsigned char *id,
160                                        SilcUInt32 id_len, SilcIdType type)
161 {
162   SilcBuffer buffer;
163
164   buffer = silc_buffer_alloc_size(4 + id_len);
165   if (!buffer)
166     return NULL;
167   silc_buffer_format(buffer,
168                      SILC_STR_UI_SHORT(type),
169                      SILC_STR_UI_SHORT(id_len),
170                      SILC_STR_DATA(id, id_len),
171                      SILC_STR_END);
172   return buffer;
173 }
174
175 /* Free ID Payload */
176
177 void silc_id_payload_free(SilcIDPayload payload)
178 {
179   if (payload) {
180     silc_free(payload->id);
181     silc_free(payload);
182   }
183 }
184
185 /* Get ID type */
186
187 SilcIdType silc_id_payload_get_type(SilcIDPayload payload)
188 {
189   return payload ? payload->type : 0;
190 }
191
192 /* Get ID */
193
194 SilcBool silc_id_payload_get_id(SilcIDPayload payload, void *ret_id,
195                                 SilcUInt32 ret_id_len)
196 {
197   if (!payload)
198     return FALSE;
199   return silc_id_str2id(payload->id, payload->len, payload->type,
200                         ret_id, ret_id_len);
201 }
202
203 /* Get raw ID data. Data is duplicated. */
204
205 unsigned char *silc_id_payload_get_data(SilcIDPayload payload)
206 {
207   if (!payload)
208     return NULL;
209
210   return silc_memdup(payload->id, payload->len);
211 }
212
213 /* Get length of ID */
214
215 SilcUInt32 silc_id_payload_get_len(SilcIDPayload payload)
216 {
217   return payload ? payload->len : 0;
218 }
219
220 /* Converts ID to string. */
221
222 SilcBool silc_id_id2str(const void *id, SilcIdType type,
223                         unsigned char *ret_id, SilcUInt32 ret_id_size,
224                         SilcUInt32 *ret_id_len)
225 {
226   SilcServerID *server_id;
227   SilcClientID *client_id;
228   SilcChannelID *channel_id;
229   SilcUInt32 id_len = silc_id_get_len(id, type);
230
231   if (id_len > ret_id_size)
232     return FALSE;
233
234   if (ret_id_len)
235     *ret_id_len = id_len;
236
237   if (id_len > SILC_PACKET_MAX_ID_LEN)
238     return FALSE;
239
240   switch(type) {
241   case SILC_ID_SERVER:
242     server_id = (SilcServerID *)id;
243     memcpy(ret_id, server_id->ip.data, server_id->ip.data_len);
244     SILC_PUT16_MSB(server_id->port, &ret_id[server_id->ip.data_len]);
245     SILC_PUT16_MSB(server_id->rnd, &ret_id[server_id->ip.data_len + 2]);
246     return TRUE;
247     break;
248   case SILC_ID_CLIENT:
249     client_id = (SilcClientID *)id;
250     memcpy(ret_id, client_id->ip.data, client_id->ip.data_len);
251     ret_id[client_id->ip.data_len] = client_id->rnd;
252     memcpy(&ret_id[client_id->ip.data_len + 1], client_id->hash,
253            CLIENTID_HASH_LEN);
254     return TRUE;
255     break;
256   case SILC_ID_CHANNEL:
257     channel_id = (SilcChannelID *)id;
258     memcpy(ret_id, channel_id->ip.data, channel_id->ip.data_len);
259     SILC_PUT16_MSB(channel_id->port, &ret_id[channel_id->ip.data_len]);
260     SILC_PUT16_MSB(channel_id->rnd, &ret_id[channel_id->ip.data_len + 2]);
261     return TRUE;
262     break;
263   }
264
265   return FALSE;
266 }
267
268 /* Converts string to a ID */
269
270 SilcBool silc_id_str2id(const unsigned char *id, SilcUInt32 id_len,
271                         SilcIdType type, void *ret_id, SilcUInt32 ret_id_size)
272 {
273   if (id_len > SILC_PACKET_MAX_ID_LEN)
274     return FALSE;
275
276   switch(type) {
277   case SILC_ID_SERVER:
278     {
279       SilcServerID *server_id = ret_id;
280
281       if (id_len != ID_SERVER_LEN_PART + 4 &&
282           id_len != ID_SERVER_LEN_PART + 16)
283         return FALSE;
284
285       if (ret_id_size < sizeof(SilcServerID))
286         return FALSE;
287
288       memset(ret_id, 0, ret_id_size);
289       memcpy(server_id->ip.data, id, (id_len > ID_SERVER_LEN_PART + 4 ?
290                                       16 : 4));
291       server_id->ip.data_len = (id_len > ID_SERVER_LEN_PART + 4 ? 16 : 4);
292       SILC_GET16_MSB(server_id->port, &id[server_id->ip.data_len]);
293       SILC_GET16_MSB(server_id->rnd, &id[server_id->ip.data_len + 2]);
294       return TRUE;
295     }
296     break;
297   case SILC_ID_CLIENT:
298     {
299       SilcClientID *client_id = ret_id;
300
301       if (id_len != ID_CLIENT_LEN_PART + 4 &&
302           id_len != ID_CLIENT_LEN_PART + 16)
303         return FALSE;
304
305       if (ret_id_size < sizeof(SilcClientID))
306         return FALSE;
307
308       memset(ret_id, 0, ret_id_size);
309       memcpy(client_id->ip.data, id, (id_len > ID_CLIENT_LEN_PART + 4 ?
310                                       16 : 4));
311       client_id->ip.data_len = (id_len > ID_CLIENT_LEN_PART + 4 ? 16 : 4);
312       client_id->rnd = id[client_id->ip.data_len];
313       memcpy(client_id->hash, &id[client_id->ip.data_len + 1],
314              CLIENTID_HASH_LEN);
315       return TRUE;
316     }
317     break;
318   case SILC_ID_CHANNEL:
319     {
320       SilcChannelID *channel_id = ret_id;
321
322       if (id_len != ID_CHANNEL_LEN_PART + 4 &&
323           id_len != ID_CHANNEL_LEN_PART + 16)
324         return FALSE;
325
326       if (ret_id_size < sizeof(SilcChannelID))
327         return FALSE;
328
329       memset(ret_id, 0, ret_id_size);
330       memcpy(channel_id->ip.data, id, (id_len > ID_CHANNEL_LEN_PART + 4 ?
331                                        16 : 4));
332       channel_id->ip.data_len = (id_len > ID_CHANNEL_LEN_PART + 4 ? 16 : 4);
333       SILC_GET16_MSB(channel_id->port, &id[channel_id->ip.data_len]);
334       SILC_GET16_MSB(channel_id->rnd, &id[channel_id->ip.data_len + 2]);
335       return TRUE;
336     }
337     break;
338   }
339
340   return FALSE;
341 }
342
343 /* Converts string to ID */
344
345 SilcBool silc_id_str2id2(const unsigned char *id, SilcUInt32 id_len,
346                          SilcIdType type, SilcID *ret_id)
347 {
348   if (!ret_id)
349     return FALSE;
350
351   ret_id->type = type;
352
353   switch (type) {
354   case SILC_ID_CLIENT:
355     return silc_id_str2id(id, id_len, type, &ret_id->u.client_id,
356                           sizeof(ret_id->u.client_id));
357     break;
358
359   case SILC_ID_SERVER:
360     return silc_id_str2id(id, id_len, type, &ret_id->u.server_id,
361                           sizeof(ret_id->u.server_id));
362     break;
363
364   case SILC_ID_CHANNEL:
365     return silc_id_str2id(id, id_len, type, &ret_id->u.channel_id,
366                           sizeof(ret_id->u.channel_id));
367     break;
368   }
369
370   return FALSE;
371 }
372
373 /* Returns length of the ID */
374
375 SilcUInt32 silc_id_get_len(const void *id, SilcIdType type)
376 {
377   switch(type) {
378   case SILC_ID_SERVER:
379     {
380       SilcServerID *server_id = (SilcServerID *)id;
381       return ID_SERVER_LEN_PART + server_id->ip.data_len;
382     }
383     break;
384   case SILC_ID_CLIENT:
385     {
386       SilcClientID *client_id = (SilcClientID *)id;
387       return ID_CLIENT_LEN_PART + client_id->ip.data_len;
388     }
389     break;
390   case SILC_ID_CHANNEL:
391     {
392       SilcChannelID *channel_id = (SilcChannelID *)id;
393       return ID_CHANNEL_LEN_PART + channel_id->ip.data_len;
394     }
395     break;
396   }
397
398   return 0;
399 }
400
401 /* Duplicate ID data */
402
403 void *silc_id_dup(const void *id, SilcIdType type)
404 {
405   switch(type) {
406   case SILC_ID_SERVER:
407     {
408       SilcServerID *server_id = (SilcServerID *)id;
409       return silc_memdup(server_id, sizeof(*server_id));
410     }
411     break;
412   case SILC_ID_CLIENT:
413     {
414       SilcClientID *client_id = (SilcClientID *)id;
415       return silc_memdup(client_id, sizeof(*client_id));
416     }
417     break;
418   case SILC_ID_CHANNEL:
419     {
420       SilcChannelID *channel_id = (SilcChannelID *)id;
421       return silc_memdup(channel_id, sizeof(*channel_id));
422     }
423     break;
424   }
425
426   return NULL;
427 }