Merged silc_1_0_branch to trunk.
[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 - 8) ||
113         (newp->id_len < 1 || newp->id_len > buffer.len - 8) ||
114         (newp->id_len + newp->name_len > buffer.len - 8)) {
115       SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
116       goto err;
117     }
118
119     len = 2 + newp->name_len + 2 + newp->id_len + 4;
120     if (buffer.len < len)
121       break;
122     silc_buffer_pull(&buffer, len);
123
124     silc_dlist_add(list, newp);
125   }
126   
127   return list;
128
129  err:
130   silc_channel_payload_list_free(list);
131   return NULL;
132 }
133
134 /* Encode new channel payload and returns it as buffer. */
135
136 SilcBuffer silc_channel_payload_encode(const unsigned char *channel_name,
137                                        SilcUInt16 channel_name_len,
138                                        const unsigned char *channel_id,
139                                        SilcUInt32 channel_id_len,
140                                        SilcUInt32 mode)
141 {
142   SilcBuffer buffer;
143
144   SILC_LOG_DEBUG(("Encoding message payload"));
145
146   buffer = silc_buffer_alloc_size(2 + channel_name_len + 2 + 
147                                   channel_id_len + 4);
148   if (!buffer)
149     return NULL;
150
151   /* Encode the Channel Payload */
152   silc_buffer_format(buffer, 
153                      SILC_STR_UI_SHORT(channel_name_len),
154                      SILC_STR_UI_XNSTRING(channel_name, channel_name_len),
155                      SILC_STR_UI_SHORT(channel_id_len),
156                      SILC_STR_UI_XNSTRING(channel_id, channel_id_len),
157                      SILC_STR_UI_INT(mode),
158                      SILC_STR_END);
159
160   return buffer;
161 }
162
163 /* Frees Channel Payload */
164
165 void silc_channel_payload_free(SilcChannelPayload payload)
166 {
167   silc_free(payload->channel_name);
168   silc_free(payload->channel_id);
169   silc_free(payload);
170 }
171
172 /* Free's list of Channel Payloads */
173
174 void silc_channel_payload_list_free(SilcDList list)
175 {
176   SilcChannelPayload entry;
177
178   silc_dlist_start(list);
179   while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
180     silc_free(entry->channel_name);
181     silc_free(entry->channel_id);
182     silc_dlist_del(list, entry);
183     silc_free(entry);
184   }
185
186   silc_dlist_uninit(list);
187 }
188
189 /* Return the channel name */
190
191 unsigned char *silc_channel_get_name(SilcChannelPayload payload,
192                                      SilcUInt32 *channel_name_len)
193 {
194   if (channel_name_len)
195     *channel_name_len = payload->name_len;
196
197   return payload->channel_name;
198 }
199
200 /* Return the channel ID */
201
202 unsigned char *silc_channel_get_id(SilcChannelPayload payload,
203                                    SilcUInt32 *channel_id_len)
204 {
205   if (channel_id_len)
206     *channel_id_len = payload->id_len;
207
208   return payload->channel_id;
209 }
210
211 /* Return the channel ID as parsed ID. */
212
213 SilcChannelID *silc_channel_get_id_parse(SilcChannelPayload payload)
214 {
215   return silc_id_str2id(payload->channel_id, payload->id_len,
216                         SILC_ID_CHANNEL);
217 }
218
219 /* Return the mode. The mode is arbitrary. It can be the mode of the
220    channel or perhaps the mode of the client on the channel.  The protocol
221    dictates what the usage of the mode is in different circumstances. */
222
223 SilcUInt32 silc_channel_get_mode(SilcChannelPayload payload)
224 {
225   return payload->mode;
226 }
227
228
229 /******************************************************************************
230
231                              Channel Key Payload
232
233 ******************************************************************************/
234
235 /* Channel Key Payload structrue. Channel keys are parsed from SILC
236    packets into this structure. */
237 struct SilcChannelKeyPayloadStruct {
238   unsigned char *id;
239   unsigned char *cipher;
240   unsigned char *key;
241   SilcUInt16 id_len;
242   SilcUInt16 cipher_len;
243   SilcUInt16 key_len;
244 };
245
246 /* Parses channel key payload returning new channel key payload structure */
247
248 SilcChannelKeyPayload 
249 silc_channel_key_payload_parse(const unsigned char *payload,
250                                SilcUInt32 payload_len)
251 {
252   SilcBufferStruct buffer;
253   SilcChannelKeyPayload newp;
254   int ret;
255
256   SILC_LOG_DEBUG(("Parsing channel key payload"));
257
258   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
259   newp = silc_calloc(1, sizeof(*newp));
260   if (!newp)
261     return NULL;
262
263   /* Parse the Channel Key Payload */
264   ret =
265     silc_buffer_unformat(&buffer,
266                          SILC_STR_UI16_NSTRING_ALLOC(&newp->id, &newp->id_len),
267                          SILC_STR_UI16_NSTRING_ALLOC(&newp->cipher, 
268                                                      &newp->cipher_len),
269                          SILC_STR_UI16_NSTRING_ALLOC(&newp->key, 
270                                                      &newp->key_len),
271                          SILC_STR_END);
272   if (ret == -1)
273     goto err;
274
275   if (newp->id_len < 1 || newp->key_len < 1 || newp->cipher_len < 1 ||
276       newp->id_len + newp->cipher_len + newp->key_len > buffer.len - 6) {
277     SILC_LOG_ERROR(("Incorrect channel key payload in packet"));
278     goto err;
279   }
280
281   return newp;
282
283  err:
284   if (newp->id)
285     silc_free(newp->id);
286   if (newp->cipher)
287     silc_free(newp->cipher);
288   if (newp->key)
289     silc_free(newp->key);
290   silc_free(newp);
291   return NULL;
292 }
293
294 /* Encodes channel key payload into a buffer and returns it. This is used 
295    to add channel key payload into a packet. */
296
297 SilcBuffer silc_channel_key_payload_encode(SilcUInt16 id_len,
298                                            const unsigned char *id,
299                                            SilcUInt16 cipher_len,
300                                            const unsigned char *cipher,
301                                            SilcUInt16 key_len,
302                                            const unsigned char *key)
303 {
304   SilcBuffer buffer;
305   SilcUInt32 len;
306
307   SILC_LOG_DEBUG(("Encoding channel key payload"));
308
309   /* Allocate channel payload buffer. Length is 2 + id + 2 + key + 
310      2 + cipher */
311   len = 2 + id_len + 2 + key_len + 2 + cipher_len;
312   buffer = silc_buffer_alloc_size(len);
313   if (!buffer)
314     return NULL;
315
316   /* Encode the Channel Payload */
317   silc_buffer_format(buffer, 
318                      SILC_STR_UI_SHORT(id_len),
319                      SILC_STR_UI_XNSTRING(id, id_len),
320                      SILC_STR_UI_SHORT(cipher_len),
321                      SILC_STR_UI_XNSTRING(cipher, cipher_len),
322                      SILC_STR_UI_SHORT(key_len),
323                      SILC_STR_UI_XNSTRING(key, key_len),
324                      SILC_STR_END);
325
326   return buffer;
327 }
328
329 /* Frees Channel Key Payload */
330
331 void silc_channel_key_payload_free(SilcChannelKeyPayload payload)
332 {
333   if (payload) {
334     silc_free(payload->id);
335     silc_free(payload->cipher);
336     if (payload->key) {
337       memset(payload->key, 0, payload->key_len);
338       silc_free(payload->key);
339     }
340     silc_free(payload);
341   }
342 }
343
344 /* Return ID */
345
346 unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, 
347                                        SilcUInt32 *id_len)
348 {
349   if (id_len)
350     *id_len = payload->id_len;
351
352   return payload->id;
353 }
354
355 /* Return cipher name */
356
357 unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
358                                            SilcUInt32 *cipher_len)
359 {
360   if (cipher_len)
361     *cipher_len = payload->cipher_len;
362
363   return payload->cipher;
364 }
365
366 /* Return key */
367
368 unsigned char *silc_channel_key_get_key(SilcChannelKeyPayload payload,
369                                         SilcUInt32 *key_len)
370 {
371   if (key_len)
372     *key_len = payload->key_len;
373
374   return payload->key;
375 }