updated XML interface to new schema
[strongswan.git] / src / charon / control / interfaces / xml_interface.c
1 /**
2 * @file xml_interface.c
3 *
4 * @brief Implementation of xml_interface_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include <stdlib.h>
24
25 #include "xml_interface.h"
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <pthread.h>
34 #include <signal.h>
35 #include <libxml/xmlreader.h>
36 #include <libxml/xmlwriter.h>
37
38 #include <library.h>
39 #include <daemon.h>
40 #include <processing/jobs/callback_job.h>
41
42
43 typedef struct private_xml_interface_t private_xml_interface_t;
44
45 /**
46 * Private data of an xml_interface_t object.
47 */
48 struct private_xml_interface_t {
49
50 /**
51 * Public part of xml_t object.
52 */
53 xml_interface_t public;
54
55 /**
56 * XML unix socket fd
57 */
58 int socket;
59
60 /**
61 * job accepting stroke messages
62 */
63 callback_job_t *job;
64 };
65
66 ENUM(ike_sa_state_lower_names, IKE_CREATED, IKE_DELETING,
67 "created",
68 "connecting",
69 "established",
70 "rekeying",
71 "deleting",
72 );
73
74 /**
75 * write a bool into element
76 */
77 static void write_bool(xmlTextWriterPtr writer, char *element, bool val)
78 {
79 xmlTextWriterWriteElement(writer, element, val ? "true" : "false");
80 }
81
82 /**
83 * write a identification_t into element
84 */
85 static void write_id(xmlTextWriterPtr writer, char *element, identification_t *id)
86 {
87 xmlTextWriterStartElement(writer, element);
88 switch (id->get_type(id))
89 {
90 {
91 char *type = "";
92 while (TRUE)
93 {
94 case ID_IPV4_ADDR:
95 type = "ipv4";
96 break;
97 case ID_IPV6_ADDR:
98 type = "ipv6";
99 break;
100 case ID_FQDN:
101 type = "fqdn";
102 break;
103 case ID_RFC822_ADDR:
104 type = "email";
105 break;
106 case ID_DER_ASN1_DN:
107 type = "asn1dn";
108 break;
109 case ID_DER_ASN1_GN:
110 type = "asn1gn";
111 break;
112 }
113 xmlTextWriterWriteAttribute(writer, "type", type);
114 xmlTextWriterWriteFormatString(writer, "%D", id);
115 break;
116 }
117 case ID_ANY:
118 xmlTextWriterWriteAttribute(writer, "type", "any");
119 break;
120 default:
121 /* TODO: base64 keyid */
122 xmlTextWriterWriteAttribute(writer, "type", "keyid");
123 break;
124 }
125 xmlTextWriterEndElement(writer);
126 }
127
128 /**
129 * write a host_t address into an element
130 */
131 static void write_address(xmlTextWriterPtr writer, char *element, host_t *host)
132 {
133 xmlTextWriterStartElement(writer, element);
134 xmlTextWriterWriteAttribute(writer, "type",
135 host->get_family(host) == AF_INET ? "ipv4" : "ipv6");
136 if (host->is_anyaddr(host))
137 { /* do not use %any for XML */
138 xmlTextWriterWriteFormatString(writer, "%s",
139 host->get_family(host) == AF_INET ? "0.0.0.0" : "::");
140 }
141 else
142 {
143 xmlTextWriterWriteFormatString(writer, "%H", host);
144 }
145 xmlTextWriterEndElement(writer);
146 }
147
148 /**
149 * process a ikesalist query request message
150 */
151 static void request_query_ikesa(xmlTextReaderPtr reader, xmlTextWriterPtr writer)
152 {
153 iterator_t *iterator;
154 ike_sa_t *ike_sa;
155
156 /* <ikesalist> */
157 xmlTextWriterStartElement(writer, "ikesalist");
158
159 iterator = charon->ike_sa_manager->create_iterator(charon->ike_sa_manager);
160 while (iterator->iterate(iterator, (void**)&ike_sa))
161 {
162 ike_sa_id_t *id;
163 host_t *local, *remote;
164 iterator_t *children;
165 child_sa_t *child_sa;
166
167 id = ike_sa->get_id(ike_sa);
168
169 xmlTextWriterStartElement(writer, "ikesa");
170 xmlTextWriterWriteFormatElement(writer, "id", "%d",
171 ike_sa->get_unique_id(ike_sa));
172 xmlTextWriterWriteFormatElement(writer, "status", "%N",
173 ike_sa_state_lower_names, ike_sa->get_state(ike_sa));
174 xmlTextWriterWriteElement(writer, "role",
175 id->is_initiator(id) ? "initiator" : "responder");
176 xmlTextWriterWriteElement(writer, "peerconfig", ike_sa->get_name(ike_sa));
177
178 /* <local> */
179 local = ike_sa->get_my_host(ike_sa);
180 xmlTextWriterStartElement(writer, "local");
181 xmlTextWriterWriteFormatElement(writer, "spi", "%.16llx",
182 id->is_initiator(id) ? id->get_initiator_spi(id)
183 : id->get_responder_spi(id));
184 write_id(writer, "identification", ike_sa->get_my_id(ike_sa));
185 write_address(writer, "address", local);
186 xmlTextWriterWriteFormatElement(writer, "port", "%d",
187 local->get_port(local));
188 if (ike_sa->supports_extension(ike_sa, EXT_NATT))
189 {
190 write_bool(writer, "nat", ike_sa->has_condition(ike_sa, COND_NAT_HERE));
191 }
192 /* </local> */
193
194 /* <remote> */
195 remote = ike_sa->get_other_host(ike_sa);
196 xmlTextWriterStartElement(writer, "remote");
197 xmlTextWriterWriteFormatElement(writer, "spi", "%.16llx",
198 id->is_initiator(id) ? id->get_responder_spi(id)
199 : id->get_initiator_spi(id));
200 write_id(writer, "identification", ike_sa->get_other_id(ike_sa));
201 write_address(writer, "address", remote);
202 xmlTextWriterWriteFormatElement(writer, "port", "%d",
203 remote->get_port(remote));
204 if (ike_sa->supports_extension(ike_sa, EXT_NATT))
205 {
206 write_bool(writer, "nat", ike_sa->has_condition(ike_sa, COND_NAT_THERE));
207 }
208 /* </remote> */
209
210 /* <childsalist> */
211 xmlTextWriterStartElement(writer, "childsalist");
212 children = ike_sa->create_child_sa_iterator(ike_sa);
213 while (children->iterate(children, (void**)&child_sa))
214 {
215 /* TODO: Children */
216 }
217 children->destroy(children);
218 /* </childsalist> */
219 xmlTextWriterEndElement(writer);
220
221 /* </ikesa> */
222 xmlTextWriterEndElement(writer);
223 }
224 iterator->destroy(iterator);
225
226 /* </ikesalist> */
227 xmlTextWriterEndElement(writer);
228 }
229
230 /**
231 * process a query request
232 */
233 static void request_query(xmlTextReaderPtr reader, xmlTextWriterPtr writer)
234 {
235 /* <query> */
236 xmlTextWriterStartElement(writer, "query");
237 while (xmlTextReaderRead(reader))
238 {
239 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT)
240 {
241 if (streq(xmlTextReaderConstName(reader), "ikesalist"))
242 {
243 request_query_ikesa(reader, writer);
244 break;
245 }
246 }
247 }
248 /* </query> */
249 xmlTextWriterEndElement(writer);
250 }
251
252 /**
253 * process a request message
254 */
255 static void request(xmlTextReaderPtr reader, char *id, int fd)
256 {
257 xmlTextWriterPtr writer;
258
259 writer = xmlNewTextWriter(xmlOutputBufferCreateFd(fd, NULL));
260 if (writer == NULL)
261 {
262 DBG1(DBG_CFG, "opening SMP XML writer failed");
263 return;
264 }
265
266 xmlTextWriterStartDocument(writer, NULL, NULL, NULL);
267 /* <message xmlns="http://www.strongswan.org/smp/1.0"
268 id="id" type="response"> */
269 xmlTextWriterStartElement(writer, "message");
270 xmlTextWriterWriteAttribute(writer, "xmlns",
271 "http://www.strongswan.org/smp/1.0");
272 xmlTextWriterWriteAttribute(writer, "id", id);
273 xmlTextWriterWriteAttribute(writer, "type", "response");
274
275 while (xmlTextReaderRead(reader))
276 {
277 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT)
278 {
279 if (streq(xmlTextReaderConstName(reader), "query"))
280 {
281 request_query(reader, writer);
282 break;
283 }
284 }
285 }
286 /* </message> and close document */
287 xmlTextWriterEndDocument(writer);
288 xmlFreeTextWriter(writer);
289 /* write a newline to indicate end of xml */
290 write(fd, "\n", 1);
291 }
292
293 /**
294 * read from a opened connection and process it
295 */
296 static job_requeue_t process(int *fdp)
297 {
298 int oldstate, fd = *fdp;
299 char buffer[4096];
300 size_t len;
301 xmlTextReaderPtr reader;
302 char *id = NULL, *type = NULL;
303
304 pthread_cleanup_push((void*)close, (void*)fd);
305 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
306 len = read(fd, buffer, sizeof(buffer));
307 pthread_setcancelstate(oldstate, NULL);
308 pthread_cleanup_pop(0);
309 if (len <= 0)
310 {
311 close(fd);
312 DBG2(DBG_CFG, "SMP XML connection closed");
313 return JOB_REQUEUE_NONE;
314 }
315
316 reader = xmlReaderForMemory(buffer, len, NULL, NULL, 0);
317 if (reader == NULL)
318 {
319 DBG1(DBG_CFG, "opening SMP XML reader failed");
320 return JOB_REQUEUE_FAIR;;
321 }
322
323 /* read message type and id */
324 while (xmlTextReaderRead(reader))
325 {
326 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT &&
327 streq(xmlTextReaderConstName(reader), "message"))
328 {
329 id = xmlTextReaderGetAttribute(reader, "id");
330 type = xmlTextReaderGetAttribute(reader, "type");
331 break;
332 }
333 }
334
335 /* process message */
336 if (id && type)
337 {
338 if (streq(type, "request"))
339 {
340 request(reader, id, fd);
341 }
342 else
343 {
344 /* response(reader, id) */
345 }
346 }
347 xmlFreeTextReader(reader);
348 return JOB_REQUEUE_FAIR;;
349 }
350
351 /**
352 * accept from XML socket and create jobs to process connections
353 */
354 static job_requeue_t dispatch(private_xml_interface_t *this)
355 {
356 struct sockaddr_un strokeaddr;
357 int oldstate, fd, *fdp, strokeaddrlen = sizeof(strokeaddr);
358 callback_job_t *job;
359
360 /* wait for connections, but allow thread to terminate */
361 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
362 fd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen);
363 pthread_setcancelstate(oldstate, NULL);
364
365 if (fd < 0)
366 {
367 DBG1(DBG_CFG, "accepting SMP XML socket failed: %s", strerror(errno));
368 sleep(1);
369 return JOB_REQUEUE_FAIR;;
370 }
371
372 fdp = malloc_thing(int);
373 *fdp = fd;
374 job = callback_job_create((callback_job_cb_t)process, fdp, free, this->job);
375 charon->processor->queue_job(charon->processor, (job_t*)job);
376
377 return JOB_REQUEUE_DIRECT;
378 }
379
380 struct sockaddr_un unix_addr = { AF_UNIX, "/var/run/charon.xml"};
381
382 /**
383 * Implementation of itnerface_t.destroy.
384 */
385 static void destroy(private_xml_interface_t *this)
386 {
387 this->job->cancel(this->job);
388 close(this->socket);
389 //unlink(unix_addr.sun_path);
390 free(this);
391 }
392
393 /*
394 * Described in header file
395 */
396 interface_t *interface_create()
397 {
398 private_xml_interface_t *this = malloc_thing(private_xml_interface_t);
399 //mode_t old;
400 struct sockaddr_in tcp_addr;
401
402 this->public.interface.destroy = (void (*)(interface_t*))destroy;
403
404 /* set up unix socket */
405 this->socket = socket(AF_INET, SOCK_STREAM, 0);//socket(AF_UNIX, SOCK_STREAM, 0);
406 if (this->socket == -1)
407 {
408 DBG1(DBG_CFG, "could not create XML socket");
409 free(this);
410 return NULL;
411 }
412
413 memset(&tcp_addr, 0, sizeof(tcp_addr));
414 tcp_addr.sin_family = AF_INET;
415 tcp_addr.sin_addr.s_addr = INADDR_ANY;
416 tcp_addr.sin_port = htons(4502);
417 if (bind(this->socket, (struct sockaddr*)&tcp_addr, sizeof(tcp_addr)) < 0)
418 {
419 DBG1(DBG_CFG, "could not bind XML socket: %s", strerror(errno));
420 close(this->socket);
421 free(this);
422 return NULL;
423 }
424
425 /*
426 old = umask(~S_IRWXU);
427 if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
428 {
429 DBG1(DBG_CFG, "could not bind XML socket: %s", strerror(errno));
430 close(this->socket);
431 free(this);
432 return NULL;
433 }
434 umask(old);*/
435
436 if (listen(this->socket, 5) < 0)
437 {
438 DBG1(DBG_CFG, "could not listen on XML socket: %s", strerror(errno));
439 close(this->socket);
440 free(this);
441 return NULL;
442 }
443
444 this->job = callback_job_create((callback_job_cb_t)dispatch, this, NULL, NULL);
445 charon->processor->queue_job(charon->processor, (job_t*)this->job);
446
447 return &this->public.interface;
448 }
449