Merged from silc_1_0_branch.
[silc.git] / lib / silccore / silcchannel.c
1 /*
2
3   silcchannel.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2002 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 /* Channel Payload and Channel Key Payload implementations. */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23 #include "silcchannel.h"
24
25 /******************************************************************************
26
27                               Channel Payload
28
29 ******************************************************************************/
30
31 /* Channel Message Payload structure. Contents of this structure is parsed
32    from SILC packets. */
33 struct SilcChannelPayloadStruct {
34   unsigned char *channel_name;
35   unsigned char *channel_id;
36   SilcUInt32 mode;
37   SilcUInt16 name_len;
38   SilcUInt16 id_len;
39 };
40
41 /* Parses channel payload returning new channel payload structure. */
42
43 SilcChannelPayload silc_channel_payload_parse(const unsigned char *payload,
44                                               SilcUInt32 payload_len)
45 {
46   SilcBufferStruct buffer;
47   SilcChannelPayload newp;
48   int ret;
49
50   SILC_LOG_DEBUG(("Parsing channel payload"));
51
52   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
53   newp = silc_calloc(1, sizeof(*newp));
54   if (!newp)
55     return NULL;
56
57   /* Parse the Channel Payload. Ignore the padding. */
58   ret = silc_buffer_unformat(&buffer,
59                              SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_name, 
60                                                          &newp->name_len),
61                              SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_id, 
62                                                          &newp->id_len),
63                              SILC_STR_UI_INT(&newp->mode),
64                              SILC_STR_END);
65   if (ret == -1)
66     goto err;
67
68   if ((newp->name_len < 1 || newp->name_len > buffer.len - 8) ||
69       (newp->id_len < 1 || newp->id_len > buffer.len - 8) ||
70       (newp->id_len + newp->name_len > buffer.len - 8)) {
71     SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
72     goto err;
73   }
74
75   return newp;
76
77  err:
78   silc_channel_payload_free(newp);
79   return NULL;
80 }
81
82 /* Parses list of channel payloads returning list of payloads. */
83
84 SilcDList silc_channel_payload_parse_list(const unsigned char *payload,
85                                           SilcUInt32 payload_len)
86 {
87   SilcBufferStruct buffer;
88   SilcDList list;
89   SilcChannelPayload newp;
90   SilcUInt32 len;
91   int ret;
92
93   SILC_LOG_DEBUG(("Parsing channel payload list"));
94
95   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
96   list = silc_dlist_init();
97
98   while (buffer.len) {
99     newp = silc_calloc(1, sizeof(*newp));
100     if (!newp)
101       goto err;
102     ret = silc_buffer_unformat(&buffer,
103                                SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_name, 
104                                                            &newp->name_len),
105                                SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_id, 
106                                                            &newp->id_len),
107                                SILC_STR_UI_INT(&newp->mode),
108                                SILC_STR_END);
109     if (ret == -1)
110       goto err;
111
112     if ((newp->name_len < 1 || newp->name_len > buffer.len) ||
113         (newp->id_len < 1 || newp->id_len > buffer.len)) {
114       SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
115       goto err;
116     }
117
118     len = 2 + newp->name_len + 2 + newp->id_len + 4;
119     if (buffer.len < len)
120       break;
121     silc_buffer_pull(&buffer, len);
122
123     silc_dlist_add(list, newp);
124   }
125   
126   return list;
127
128  err:
129   silc_channel_payload_list_free(list);
130   return NULL;
131 }
132
133 /* Encode new channel payload and returns it as buffer. */
134
135 SilcBuffer silc_channel_payload_encode(const unsigned char *channel_name,
136                                        SilcUInt16 channel_name_len,
137                                        const unsigned char *channel_id,
138                                        SilcUInt32 channel_id_len,
139                                        SilcUInt32 mode)
140 {
141   SilcBuffer buffer;
142
143   SILC_LOG_DEBUG(("Encoding message payload"));
144
145   buffer = silc_buffer_alloc_size(2 + channel_name_len + 2 + 
146                                   channel_id_len + 4);
147   if (!buffer)
148     return NULL;
149
150   /* Encode the Channel Payload */
151   silc_buffer_format(buffer, 
152                      SILC_STR_UI_SHORT(channel_name_len),
153                      SILC_STR_UI_XNSTRING(channel_name, channel_name_len),
154                      SILC_STR_UI_SHORT(channel_id_len),
155                      SILC_STR_UI_XNSTRING(channel_id, channel_id_len),
156                      SILC_STR_UI_INT(mode),
157                      SILC_STR_END);
158
159   return buffer;
160 }
161
162 /* Frees Channel Payload */
163
164 void silc_channel_payload_free(SilcChannelPayload payload)
165 {
166   silc_free(payload->channel_name);
167   silc_free(payload->channel_id);
168   silc_free(payload);
169 }
170
171 /* Free's list of Channel Payloads */
172
173 void silc_channel_payload_list_free(SilcDList list)
174 {
175   SilcChannelPayload entry;
176
177   silc_dlist_start(list);
178   while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
179     silc_free(entry->channel_name);
180     silc_free(entry->channel_id);
181     silc_dlist_del(list, entry);
182     silc_free(entry);
183   }
184
185   silc_dlist_uninit(list);
186 }
187
188 /* Return the channel name */
189
190 unsigned char *silc_channel_get_name(SilcChannelPayload payload,
191                                      SilcUInt32 *channel_name_len)
192 {
193   if (channel_name_len)
194     *channel_name_len = payload->name_len;
195
196   return payload->channel_name;
197 }
198
199 /* Return the channel ID */
200
201 unsigned char *silc_channel_get_id(SilcChannelPayload payload,
202                                    SilcUInt32 *channel_id_len)
203 {
204   if (channel_id_len)
205     *channel_id_len = payload->id_len;
206
207   return payload->channel_id;
208 }
209
210 /* Return the channel ID as parsed ID. */
211
212 SilcChannelID *silc_channel_get_id_parse(SilcChannelPayload payload)
213 {
214   return silc_id_str2id(payload->channel_id, payload->id_len,
215                         SILC_ID_CHANNEL);
216 }
217
218 /* Return the mode. The mode is arbitrary. It can be the mode of the
219    channel or perhaps the mode of the client on the channel.  The protocol
220    dictates what the usage of the mode is in different circumstances. */
221
222 SilcUInt32 silc_channel_get_mode(SilcChannelPayload payload)
223 {
224   return payload->mode;
225 }
226
227
228 /******************************************************************************
229
230                              Channel Key Payload
231
232 ******************************************************************************/
233
234 /* Channel Key Payload structrue. Channel keys are parsed from SILC
235    packets into this structure. */
236 struct SilcChannelKeyPayloadStruct {
237   unsigned char *id;
238   unsigned char *cipher;
239   unsigned char *key;
240   SilcUInt16 id_len;
241   SilcUInt16 cipher_len;
242   SilcUInt16 key_len;
243 };
244
245 /* Parses channel key payload returning new channel key payload structure */
246
247 SilcChannelKeyPayload 
248 silc_channel_key_payload_parse(const unsigned char *payload,
249                                SilcUInt32 payload_len)
250 {
251   SilcBufferStruct buffer;
252   SilcChannelKeyPayload newp;
253   int ret;
254
255   SILC_LOG_DEBUG(("Parsing channel key payload"));
256
257   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
258   newp = silc_calloc(1, sizeof(*newp));
259   if (!newp)
260     return NULL;
261
262   /* Parse the Channel Key Payload */
263   ret =
264     silc_buffer_unformat(&buffer,
265                          SILC_STR_UI16_NSTRING_ALLOC(&newp->id, &newp->id_len),
266                          SILC_STR_UI16_NSTRING_ALLOC(&newp->cipher, 
267                                                      &newp->cipher_len),
268                          SILC_STR_UI16_NSTRING_ALLOC(&newp->key, 
269                                                      &newp->key_len),
270                          SILC_STR_END);
271   if (ret == -1)
272     goto err;
273
274   if (newp->id_len < 1 || newp->key_len < 1 || newp->cipher_len < 1 ||
275       newp->id_len + newp->cipher_len + newp->key_len > buffer.len - 6) {
276     SILC_LOG_ERROR(("Incorrect channel key payload in packet"));
277     goto err;
278   }
279
280   return newp;
281
282  err:
283   if (newp->id)
284     silc_free(newp->id);
285   if (newp->cipher)
286     silc_free(newp->cipher);
287   if (newp->key)
288     silc_free(newp->key);
289   silc_free(newp);
290   return NULL;
291 }
292
293 /* Encodes channel key payload into a buffer and returns it. This is used 
294    to add channel key payload into a packet. */
295
296 SilcBuffer silc_channel_key_payload_encode(SilcUInt16 id_len,
297                                            const unsigned char *id,
298                                            SilcUInt16 cipher_len,
299                                            const unsigned char *cipher,
300                                            SilcUInt16 key_len,
301                                            const unsigned char *key)
302 {
303   SilcBuffer buffer;
304   SilcUInt32 len;
305
306   SILC_LOG_DEBUG(("Encoding channel key payload"));
307
308   /* Allocate channel payload buffer. Length is 2 + id + 2 + key + 
309      2 + cipher */
310   len = 2 + id_len + 2 + key_len + 2 + cipher_len;
311   buffer = silc_buffer_alloc_size(len);
312   if (!buffer)
313     return NULL;
314
315   /* Encode the Channel Payload */
316   silc_buffer_format(buffer, 
317                      SILC_STR_UI_SHORT(id_len),
318                      SILC_STR_UI_XNSTRING(id, id_len),
319                      SILC_STR_UI_SHORT(cipher_len),
320                      SILC_STR_UI_XNSTRING(cipher, cipher_len),
321                      SILC_STR_UI_SHORT(key_len),
322                      SILC_STR_UI_XNSTRING(key, key_len),
323                      SILC_STR_END);
324
325   return buffer;
326 }
327
328 /* Frees Channel Key Payload */
329
330 void silc_channel_key_payload_free(SilcChannelKeyPayload payload)
331 {
332   if (payload) {
333     silc_free(payload->id);
334     silc_free(payload->cipher);
335     if (payload->key) {
336       memset(payload->key, 0, payload->key_len);
337       silc_free(payload->key);
338     }
339     silc_free(payload);
340   }
341 }
342
343 /* Return ID */
344
345 unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, 
346                                        SilcUInt32 *id_len)
347 {
348   if (id_len)
349     *id_len = payload->id_len;
350
351   return payload->id;
352 }
353
354 /* Return cipher name */
355
356 unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
357                                            SilcUInt32 *cipher_len)
358 {
359   if (cipher_len)
360     *cipher_len = payload->cipher_len;
361
362   return payload->cipher;
363 }
364
365 /* Return key */
366
367 unsigned char *silc_channel_key_get_key(SilcChannelKeyPayload payload,
368                                         SilcUInt32 *key_len)
369 {
370   if (key_len)
371     *key_len = payload->key_len;
372
373   return payload->key;
374 }