fixed 64bit issue with file descriptor
[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 xmlTextWriterEndElement(writer);
193 /* </local> */
194
195 /* <remote> */
196 remote = ike_sa->get_other_host(ike_sa);
197 xmlTextWriterStartElement(writer, "remote");
198 xmlTextWriterWriteFormatElement(writer, "spi", "%.16llx",
199 id->is_initiator(id) ? id->get_responder_spi(id)
200 : id->get_initiator_spi(id));
201 write_id(writer, "identification", ike_sa->get_other_id(ike_sa));
202 write_address(writer, "address", remote);
203 xmlTextWriterWriteFormatElement(writer, "port", "%d",
204 remote->get_port(remote));
205 if (ike_sa->supports_extension(ike_sa, EXT_NATT))
206 {
207 write_bool(writer, "nat", ike_sa->has_condition(ike_sa, COND_NAT_THERE));
208 }
209 xmlTextWriterEndElement(writer);
210 /* </remote> */
211
212 /* <childsalist> */
213 xmlTextWriterStartElement(writer, "childsalist");
214 children = ike_sa->create_child_sa_iterator(ike_sa);
215 while (children->iterate(children, (void**)&child_sa))
216 {
217 /* TODO: Children */
218 }
219 children->destroy(children);
220 /* </childsalist> */
221 xmlTextWriterEndElement(writer);
222
223 /* </ikesa> */
224 xmlTextWriterEndElement(writer);
225 }
226 iterator->destroy(iterator);
227
228 /* </ikesalist> */
229 xmlTextWriterEndElement(writer);
230 }
231
232 /**
233 * process a query request
234 */
235 static void request_query(xmlTextReaderPtr reader, xmlTextWriterPtr writer)
236 {
237 /* <query> */
238 xmlTextWriterStartElement(writer, "query");
239 while (xmlTextReaderRead(reader))
240 {
241 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT)
242 {
243 if (streq(xmlTextReaderConstName(reader), "ikesalist"))
244 {
245 request_query_ikesa(reader, writer);
246 break;
247 }
248 }
249 }
250 /* </query> */
251 xmlTextWriterEndElement(writer);
252 }
253
254 /**
255 * process a request message
256 */
257 static void request(xmlTextReaderPtr reader, char *id, int fd)
258 {
259 xmlTextWriterPtr writer;
260
261 writer = xmlNewTextWriter(xmlOutputBufferCreateFd(fd, NULL));
262 if (writer == NULL)
263 {
264 DBG1(DBG_CFG, "opening SMP XML writer failed");
265 return;
266 }
267
268 xmlTextWriterStartDocument(writer, NULL, NULL, NULL);
269 /* <message xmlns="http://www.strongswan.org/smp/1.0"
270 id="id" type="response"> */
271 xmlTextWriterStartElement(writer, "message");
272 xmlTextWriterWriteAttribute(writer, "xmlns",
273 "http://www.strongswan.org/smp/1.0");
274 xmlTextWriterWriteAttribute(writer, "id", id);
275 xmlTextWriterWriteAttribute(writer, "type", "response");
276
277 while (xmlTextReaderRead(reader))
278 {
279 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT)
280 {
281 if (streq(xmlTextReaderConstName(reader), "query"))
282 {
283 request_query(reader, writer);
284 break;
285 }
286 }
287 }
288 /* </message> and close document */
289 xmlTextWriterEndDocument(writer);
290 xmlFreeTextWriter(writer);
291 }
292
293 /**
294 * cleanup helper function for open file descriptors
295 */
296 static void closefdp(int *fd)
297 {
298 close(*fd);
299 }
300
301 /**
302 * read from a opened connection and process it
303 */
304 static job_requeue_t process(int *fdp)
305 {
306 int oldstate, fd = *fdp;
307 char buffer[4096];
308 size_t len;
309 xmlTextReaderPtr reader;
310 char *id = NULL, *type = NULL;
311
312 pthread_cleanup_push((void*)closefdp, (void*)&fd);
313 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
314 len = read(fd, buffer, sizeof(buffer));
315 pthread_setcancelstate(oldstate, NULL);
316 pthread_cleanup_pop(0);
317 if (len <= 0)
318 {
319 close(fd);
320 DBG2(DBG_CFG, "SMP XML connection closed");
321 return JOB_REQUEUE_NONE;
322 }
323 DBG1(DBG_CFG, "got XML request: %b", buffer, len);
324
325 reader = xmlReaderForMemory(buffer, len, NULL, NULL, 0);
326 if (reader == NULL)
327 {
328 DBG1(DBG_CFG, "opening SMP XML reader failed");
329 return JOB_REQUEUE_FAIR;;
330 }
331
332 /* read message type and id */
333 while (xmlTextReaderRead(reader))
334 {
335 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT &&
336 streq(xmlTextReaderConstName(reader), "message"))
337 {
338 id = xmlTextReaderGetAttribute(reader, "id");
339 type = xmlTextReaderGetAttribute(reader, "type");
340 break;
341 }
342 }
343
344 /* process message */
345 if (id && type)
346 {
347 if (streq(type, "request"))
348 {
349 request(reader, id, fd);
350 }
351 else
352 {
353 /* response(reader, id) */
354 }
355 }
356 xmlFreeTextReader(reader);
357 return JOB_REQUEUE_FAIR;;
358 }
359
360 /**
361 * accept from XML socket and create jobs to process connections
362 */
363 static job_requeue_t dispatch(private_xml_interface_t *this)
364 {
365 struct sockaddr_un strokeaddr;
366 int oldstate, fd, *fdp, strokeaddrlen = sizeof(strokeaddr);
367 callback_job_t *job;
368
369 /* wait for connections, but allow thread to terminate */
370 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
371 fd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen);
372 pthread_setcancelstate(oldstate, NULL);
373
374 if (fd < 0)
375 {
376 DBG1(DBG_CFG, "accepting SMP XML socket failed: %s", strerror(errno));
377 sleep(1);
378 return JOB_REQUEUE_FAIR;;
379 }
380
381 fdp = malloc_thing(int);
382 *fdp = fd;
383 job = callback_job_create((callback_job_cb_t)process, fdp, free, this->job);
384 charon->processor->queue_job(charon->processor, (job_t*)job);
385
386 return JOB_REQUEUE_DIRECT;
387 }
388
389 struct sockaddr_un unix_addr = { AF_UNIX, "/var/run/charon.xml"};
390
391 /**
392 * Implementation of itnerface_t.destroy.
393 */
394 static void destroy(private_xml_interface_t *this)
395 {
396 this->job->cancel(this->job);
397 close(this->socket);
398 //unlink(unix_addr.sun_path);
399 free(this);
400 }
401
402 /*
403 * Described in header file
404 */
405 interface_t *interface_create()
406 {
407 private_xml_interface_t *this = malloc_thing(private_xml_interface_t);
408 //mode_t old;
409 struct sockaddr_in tcp_addr;
410
411 this->public.interface.destroy = (void (*)(interface_t*))destroy;
412
413 /* set up unix socket */
414 this->socket = socket(AF_INET, SOCK_STREAM, 0);//socket(AF_UNIX, SOCK_STREAM, 0);
415 if (this->socket == -1)
416 {
417 DBG1(DBG_CFG, "could not create XML socket");
418 free(this);
419 return NULL;
420 }
421
422 memset(&tcp_addr, 0, sizeof(tcp_addr));
423 tcp_addr.sin_family = AF_INET;
424 tcp_addr.sin_addr.s_addr = INADDR_ANY;
425 tcp_addr.sin_port = htons(4502);
426 if (bind(this->socket, (struct sockaddr*)&tcp_addr, sizeof(tcp_addr)) < 0)
427 {
428 DBG1(DBG_CFG, "could not bind XML socket: %s", strerror(errno));
429 close(this->socket);
430 free(this);
431 return NULL;
432 }
433
434 /*
435 old = umask(~S_IRWXU);
436 if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
437 {
438 DBG1(DBG_CFG, "could not bind XML socket: %s", strerror(errno));
439 close(this->socket);
440 free(this);
441 return NULL;
442 }
443 umask(old);*/
444
445 if (listen(this->socket, 5) < 0)
446 {
447 DBG1(DBG_CFG, "could not listen on XML socket: %s", strerror(errno));
448 close(this->socket);
449 free(this);
450 return NULL;
451 }
452
453 this->job = callback_job_create((callback_job_cb_t)dispatch, this, NULL, NULL);
454 charon->processor->queue_job(charon->processor, (job_t*)this->job);
455
456 return &this->public.interface;
457 }
458