Progress towards parsing update messages.
[trust_router.git] / trp / trp_msg.c
1 #include <talloc.h>
2
3 #include <trp_internal.h>
4 #include <tr_debug.h>
5
6
7 /* static prototypes */
8 static void *trp_msg_body_update_new(TALLOC_CTX *mem_ctx);
9 static TRP_RC trp_parse_update(TRP_MSG *msg, json_t *jmsg);
10
11 static void *trp_msg_body_route_req_new(TALLOC_CTX *mem_ctx);
12 static TRP_RC trp_parse_route_req(TRP_MSG *msg, json_t *jmsg);
13
14
15 /* table of string names for TMT_MSG_TYPE codes */
16 struct trp_msg_type_info {
17   const char *name;
18   TRP_MSG_TYPE type;
19   void *(*allocator)(TALLOC_CTX *);
20   TRP_RC (*parser)(TRP_MSG *, json_t *);
21 };
22
23 static struct trp_msg_type_info trp_msg_type_table[] = {
24   { "update", TRP_MSG_TYPE_UPDATE, trp_msg_body_update_new, trp_parse_update },
25   { "route_req", TRP_MSG_TYPE_ROUTE_REQ, trp_msg_body_route_req_new, trp_parse_route_req },
26   { NULL, TRP_MSG_TYPE_UNKNOWN, NULL, NULL } /* must be the last entry */
27 };
28
29 /* Use talloc's dynamic type checking to verify type.
30  * By default, this will cause program abort, but can be overridden
31  * via talloc_set_abort_fn() if more graceful handling is needed. */
32 static void msg_body_type_check(TRP_MSG_TYPE msgtype, void *p)
33 {
34   switch (msgtype) {
35   case TRP_MSG_TYPE_UPDATE:
36     talloc_get_type_abort(p, TRP_MSG_BODY_UPDATE);
37     break;
38
39   case TRP_MSG_TYPE_ROUTE_REQ:
40     talloc_get_type_abort(p, TRP_MSG_BODY_ROUTE_REQ);
41     break;
42
43   default:
44     break;
45   }
46 }
47
48 /* look up an entry in the trp_msg_type_table */
49 static struct trp_msg_type_info *get_trp_msg_type_info(TRP_MSG_TYPE msgtype)
50 {
51   struct trp_msg_type_info *entry=trp_msg_type_table;
52
53   while ((entry->type != TRP_MSG_TYPE_UNKNOWN)
54         && (entry->type != msgtype)) {
55     entry ++;
56   }
57   return entry;
58 }
59
60 /* translate strings to codes */
61 TRP_MSG_TYPE trp_msg_type_from_string(const char *s)
62 {
63   struct trp_msg_type_info *entry=trp_msg_type_table;
64
65   while ((entry->type != TRP_MSG_TYPE_UNKNOWN)
66         && (strcmp(s, entry->name)!=0)) {
67     entry++;
68   }
69   return entry->type;
70 }
71
72 /* translate codes to strings (do not need to be freed) 
73  * Returns NULL on an unknown code */
74 const char *trp_msg_type_to_string(TRP_MSG_TYPE msgtype)
75 {
76   struct trp_msg_type_info *entry=get_trp_msg_type_info(msgtype);
77   return entry->name;
78 }
79
80
81 TRP_MSG *trp_msg_new(TALLOC_CTX *mem_ctx)
82 {
83   TRP_MSG *new_msg=talloc(mem_ctx, TRP_MSG);
84
85   if (new_msg != NULL) {
86     new_msg->type=TRP_MSG_TYPE_UNKNOWN;
87     new_msg->body=NULL;
88   }
89   return new_msg;
90 }
91
92 void trp_msg_destroy(TRP_MSG *msg)
93 {
94   if (msg)
95     talloc_free(msg);
96 }
97
98
99 /* JSON helpers */
100 /* Read attribute attr from msg as an integer */
101 static TRP_RC trp_get_json_integer(json_t *msg, const char *attr, int *dest)
102 {
103   return TRP_ERROR;
104 }
105
106 /* Read attribute attr from msg as a string */
107 static TRP_RC trp_get_json_string(json_t *jmsg, const char *attr, const char **dest)
108 {
109   json_error_t err;
110   json_t *obj;
111
112   obj=json_object_get(jmsg, attr);
113   if (obj == NULL) {
114     return TRP_NOPARSE;
115   }
116   /* check type */
117   if (!json_is_string(obj)) {
118     return TRP_BADTYPE;
119   }
120
121   (*dest)=json_string_value(obj);
122   return TRP_SUCCESS;
123 }
124
125
126
127 static void *trp_msg_body_update_new(TALLOC_CTX *mem_ctx)
128 {
129   TRP_MSG_BODY_UPDATE *new_body=talloc(mem_ctx, TRP_MSG_BODY_UPDATE);
130
131   if (new_body != NULL) {
132     new_body->next=NULL;
133     new_body->community=NULL;
134     new_body->realm=NULL;
135     new_body->trust_router=NULL;
136     new_body->metric=TRP_METRIC_INFINITY;
137     new_body->interval=0;
138   }
139   return new_body;
140 }
141
142 /* parse a single record */
143 static TRP_RC trp_parse_update_record(TRP_MSG_BODY_UPDATE *body, json_t *jrecord)
144 {
145   return TRP_SUCCESS;
146 }
147
148 /* Parse an update body. Creates a linked list of records as the body, allocating all but the first.
149  * Hence, msg->body must be allocated as a TRP_MSG_BODY_UPDATE. All body records will be in the
150  * talloc context of msg.
151  *
152  * An error will be returned if any unparseable records are encountered. 
153  *
154  * TODO: clean up return codes. */
155 static TRP_RC trp_parse_update(TRP_MSG *msg, json_t *jbody)
156 {
157   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
158   json_t *jrecords=NULL;
159   json_t *jval=NULL; /* for iteration */
160   size_t ii=0;
161   size_t nrec=0;
162   TRP_MSG_BODY_UPDATE *msg_body=NULL;
163   TRP_MSG_BODY_UPDATE *cur_body=NULL;
164   TRP_RC rc=TRP_ERROR;
165
166   if (msg->type != TRP_MSG_TYPE_UPDATE) {
167     rc=TRP_BADTYPE;
168     goto cleanup;
169   }
170
171   jrecords=json_object_get(jbody, "records");
172   if ((jrecords==NULL) || (!json_is_array(jrecords))) {
173     rc=TRP_NOPARSE;
174     goto cleanup;
175   }
176
177   /* process the array */
178   /* first body is already allocated by caller. Check that it is expected type */
179   msg_body=talloc_get_type(msg->body, TRP_MSG_BODY_UPDATE);
180   if (msg_body==NULL) {
181     rc=TRP_BADTYPE;
182     goto cleanup;
183   }
184
185   cur_body=msg_body;
186   nrec=json_array_size(jrecords);
187   for (ii=0; ii<nrec; ii++) {
188     jval=json_array_get(jrecords, ii);
189     if (ii>0) {
190       /* on all but the first, need to allocate space */
191       cur_body->next=trp_msg_body_update_new(tmp_ctx);
192       if (cur_body==NULL) {
193         rc=TRP_NOMEM;
194         goto cleanup;
195       }
196       cur_body=cur_body->next;
197     }
198
199     if (TRP_SUCCESS != trp_parse_update_record(cur_body, jval)) {
200       rc=TRP_NOPARSE;
201       goto cleanup;
202     }
203   }
204
205   /* Succeeded. Move all of our new allocations into the correct talloc context */
206   for (cur_body=msg_body->next; cur_body != NULL; cur_body=cur_body->next)
207     talloc_steal(msg, cur_body); /* all successfully parsed bodies belong to msg context */
208   rc=TRP_SUCCESS;
209
210 cleanup:
211   talloc_free(tmp_ctx);
212   if (rc != TRP_SUCCESS)
213     msg_body->next=NULL; /* don't leave this hanging */
214
215   return rc;
216 }
217
218
219 static void *trp_msg_body_route_req_new(TALLOC_CTX *mem_ctx)
220 {
221   TRP_MSG_BODY_ROUTE_REQ *new_body=talloc(mem_ctx, TRP_MSG_BODY_ROUTE_REQ);
222
223   if (new_body != NULL) {
224     new_body->community=NULL;
225     new_body->realm=NULL;
226   }
227   return new_body;
228 }
229
230 static TRP_RC trp_parse_route_req(TRP_MSG *msg, json_t *jbody)
231 {
232   return TRP_ERROR;
233 }
234
235
236
237 /* returns a pointer to one of the message body types, or NULL on error/unknown type */
238 static void *trp_msg_body_new(TALLOC_CTX *mem_ctx, TRP_MSG_TYPE msgtype)
239 {
240   void *new_body=NULL;
241   struct trp_msg_type_info *info=get_trp_msg_type_info(msgtype);
242
243   if (info->type==TRP_MSG_TYPE_UNKNOWN) {
244     tr_debug("trp_msg_body_new: Unknown type %d.", info->type);
245     return NULL;
246   }
247
248   new_body=info->allocator(mem_ctx);
249   msg_body_type_check(msgtype, new_body); /* aborts program on type violation */
250   return new_body;
251 }
252
253 /* call the correct parser */
254 static TRP_RC trp_parse_msg_body(TRP_MSG *msg, json_t *jbody)
255 {
256   struct trp_msg_type_info *info=get_trp_msg_type_info(msg->type);
257
258   if (info->type==TRP_MSG_TYPE_UNKNOWN) {
259     tr_debug("trp_msg_body_parse: Unknown type %d.", info->type);
260     return TRP_ERROR;
261   }
262
263   return info->parser(msg, jbody);
264 }
265
266
267 TRP_RC trp_parse_msg(TALLOC_CTX *mem_ctx, const char *buf, size_t buflen, TRP_MSG **msg) 
268 {
269   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
270   TRP_MSG *new_msg=NULL;
271   TRP_RC msg_rc=TRP_ERROR;
272   json_error_t json_err;
273   json_t *jmsg=NULL; /* handle for the whole msg */
274   json_t *jbody=NULL;
275   const char *s;
276
277   tr_debug("trp_parse_msg: parsing %d bytes", buflen);
278
279   jmsg=json_loadb(buf, buflen, 0, &json_err);
280   if (jmsg == NULL) {
281     tr_debug("trp_parse_msg: Error parsing message.");
282     msg_rc=TRP_NOPARSE;
283     goto cleanup;
284   }
285
286   /* parse the common part of the message */
287   new_msg=trp_msg_new(tmp_ctx);
288   if (new_msg == NULL) {
289     tr_debug("trp_parse_msg: Error allocating message.");
290     msg_rc=TRP_NOMEM;
291     goto cleanup;
292   }
293
294   switch (trp_get_json_string(jmsg, "message_type", &s)) {
295   case TRP_SUCCESS:
296     break;
297   case TRP_NOPARSE:
298     tr_debug("trp_parse_msg: required attribute 'message_type' not present.");
299     msg_rc=TRP_NOPARSE;
300     goto cleanup;
301   case TRP_BADTYPE:
302     tr_debug("trp_parse_msg: required attribute 'message_type' is not a string.");
303     msg_rc=TRP_NOPARSE;
304     goto cleanup;
305   default:
306     tr_debug("trp_parse_msg: error parsing 'message_type'.");
307     msg_rc=TRP_NOPARSE;
308     goto cleanup;
309   }
310   
311   tr_debug("trp_parse_msg: 'message_type' is '%s'", s);
312   new_msg->type = trp_msg_type_from_string(s);
313   if (new_msg->type==TRP_MSG_TYPE_UNKNOWN) {
314     tr_debug("trp_parse_msg: Parsing error, unknown message_type (%s).", s);
315     msg_rc=TRP_NOPARSE;
316     goto cleanup;
317   }  
318
319   new_msg->body=trp_msg_body_new(new_msg, new_msg->type);
320   if (new_msg->body==NULL) {
321     tr_debug("trp_parse_msg: Error allocating message body for message_type %d.", new_msg->type);
322     msg_rc=TRP_NOMEM;
323     goto cleanup;
324   }
325   jbody=json_object_get(jmsg, "body");
326   if (jbody==NULL) {
327     tr_debug("trp_parse_msg: Message body not found.");
328     msg_rc=TRP_NOPARSE;
329     goto cleanup;
330   }
331
332   switch (trp_parse_msg_body(new_msg, jbody)) {
333   case TRP_SUCCESS:
334     break;
335   default:
336     tr_debug("trp_parse_msg: Error parsing message body.");
337     goto cleanup;
338   }
339
340   /* success! */
341   (*msg)=new_msg;
342   new_msg=NULL;
343   talloc_steal(mem_ctx, *msg);
344
345 cleanup:
346   talloc_free(tmp_ctx);
347   return msg_rc;
348 }