Moved SILC id utility functions from utility library.
[runtime.git] / lib / silccore / silcid.c
1 /*
2
3   id.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2008 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 }
428
429 /**************************** Utility functions *****************************/
430
431 /* Hash a ID. The `user_context' is the ID type. */
432
433 SilcUInt32 silc_hash_id(void *key, void *user_context)
434 {
435   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
436   SilcUInt32 h = 0;
437   int i;
438
439   switch (id_type) {
440   case SILC_ID_CLIENT:
441     {
442       SilcClientID *id = (SilcClientID *)key;
443
444       /* The client ID is hashed by hashing the hash of the ID
445          (which is a truncated MD5 hash of the nickname) so that we
446          can access the entry from the cache with both Client ID but
447          with just a hash from the ID as well. */
448       return silc_hash_client_id_hash(id->hash, NULL);
449     }
450     break;
451   case SILC_ID_SERVER:
452     {
453       SilcServerID *id = (SilcServerID *)key;
454
455       h = id->port * id->rnd;
456       for (i = 0; i < id->ip.data_len; i++)
457         h ^= id->ip.data[i];
458
459       return h;
460     }
461     break;
462   case SILC_ID_CHANNEL:
463     {
464       SilcChannelID *id = (SilcChannelID *)key;
465
466       h = id->port * id->rnd;
467       for (i = 0; i < id->ip.data_len; i++)
468         h ^= id->ip.data[i];
469
470       return h;
471     }
472     break;
473   default:
474     break;
475   }
476
477   return h;
478 }
479
480 /* Hash Client ID's hash. */
481
482 SilcUInt32 silc_hash_client_id_hash(void *key, void *user_context)
483 {
484   int i;
485   unsigned char *hash = key;
486   SilcUInt32 h = 0, g;
487
488   for (i = 0; i < CLIENTID_HASH_LEN; i++) {
489     h = (h << 4) + hash[i];
490     if ((g = h & 0xf0000000)) {
491       h = h ^ (g >> 24);
492       h = h ^ g;
493     }
494   }
495
496   return h;
497 }
498
499 /* Compares two ID's. May be used as SilcHashTable comparison function.
500    The Client ID's compares only the hash of the Client ID not any other
501    part of the Client ID. Other ID's are fully compared. */
502
503 SilcBool silc_hash_id_compare(void *key1, void *key2, void *user_context)
504 {
505   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
506   return (id_type == SILC_ID_CLIENT ?
507           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
508           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
509 }
510
511 /* Compares two ID's. Compares full IDs. */
512
513 SilcBool silc_hash_id_compare_full(void *key1, void *key2, void *user_context)
514 {
515   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
516   return SILC_ID_COMPARE_TYPE(key1, key2, id_type);
517 }
518
519 /* Compare two Client ID's entirely and not just the hash from the ID. */
520
521 SilcBool silc_hash_client_id_compare(void *key1, void *key2,
522                                      void *user_context)
523 {
524   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
525 }