blob: d56a9f311a3053275e6a06d0c1b02597c05e0c26 [file] [log] [blame]
Radek Krejci469aab82012-07-22 18:42:20 +02001/*!
2 * \file mod_netconf.c
3 * \brief NETCONF Apache modul for Netopeer
4 * \author Tomas Cejka <cejkat@cesnet.cz>
5 * \author Radek Krejci <rkrejci@cesnet.cz>
6 * \date 2011
7 * \date 2012
8 */
9/*
10 * Copyright (C) 2011-2012 CESNET
11 *
12 * LICENSE TERMS
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in
21 * the documentation and/or other materials provided with the
22 * distribution.
23 * 3. Neither the name of the Company nor the names of its contributors
24 * may be used to endorse or promote products derived from this
25 * software without specific prior written permission.
26 *
27 * ALTERNATIVELY, provided that this notice is retained in full, this
28 * product may be distributed under the terms of the GNU General Public
29 * License (GPL) version 2 or later, in which case the provisions
30 * of the GPL apply INSTEAD OF those given above.
31 *
32 * This software is provided ``as is'', and any express or implied
33 * warranties, including, but not limited to, the implied warranties of
34 * merchantability and fitness for a particular purpose are disclaimed.
35 * In no event shall the company or contributors be liable for any
36 * direct, indirect, incidental, special, exemplary, or consequential
37 * damages (including, but not limited to, procurement of substitute
38 * goods or services; loss of use, data, or profits; or business
39 * interruption) however caused and on any theory of liability, whether
40 * in contract, strict liability, or tort (including negligence or
41 * otherwise) arising in any way out of the use of this software, even
42 * if advised of the possibility of such damage.
43 *
44 */
45
46#include "httpd.h"
47#include "http_config.h"
48#include "http_protocol.h"
49#include "http_request.h"
50#include "ap_config.h"
51#include "http_log.h"
52#include "apu.h"
53#include "apr_general.h"
54#include "apr_sha1.h"
55#include "apr_file_io.h"
56
57#include <unixd.h>
58#include <apr_base64.h>
59#include <apr_pools.h>
60#include <apr_general.h>
61#include <apr_hash.h>
62#include <apr_strings.h>
63#include <apr_thread_proc.h>
64#include <apr_signal.h>
65
66#include <sys/types.h>
67#include <sys/socket.h>
68#include <sys/un.h>
69#include <stdlib.h>
70#include <stdio.h>
71#include <poll.h>
72#include <unistd.h>
73#include <string.h>
74#include <errno.h>
75
76#include <libnetconf.h>
77#include <libxml/tree.h>
78#include <libxml/parser.h>
79
80#define MAX_PROCS 5
81#define SOCKET_FILENAME "/tmp/pcon.sock"
82#define MAX_SOCKET_CL 10
83#define BUFFER_SIZE 4096
84
85/* sleep in master process for non-blocking socket reading */
86#define SLEEP_TIME 200
87
88#ifndef offsetof
89#define offsetof(type, member) ((size_t) ((type *) 0)->member)
90#endif
91
92struct timeval timeout = { 1, 0 };
93
94#define MSG_OK 0
95#define MSG_OPEN 1
96#define MSG_DATA 2
97#define MSG_CLOSE 3
98#define MSG_ERROR 4
99#define MSG_UNKNOWN 5
100
Radek Krejci469aab82012-07-22 18:42:20 +0200101module AP_MODULE_DECLARE_DATA netconf_module;
102
103typedef struct {
104 apr_proc_t *forkproc;
105 apr_pool_t *pool;
Radek Krejcif23850c2012-07-23 16:14:17 +0200106} mod_netconf_cfg;
Radek Krejci469aab82012-07-22 18:42:20 +0200107
108volatile int isterminated = 0;
109
110static char* password;
111
112
113static void signal_handler(int sign)
114{
115 switch (sign) {
116 case SIGTERM:
117 isterminated = 1;
118 fprintf(stderr, "got TERM signal... %s\n",
119 apr_signal_description_get(sign));
120 break;
121 default:
122 printf("%s\n", apr_signal_description_get(sign));
123 break;
124 }
125}
126
Radek Krejcif23850c2012-07-23 16:14:17 +0200127server_rec *gserver;
128static int gen_ncsession_hash(char** hash, const char* hostname, const char* port, const char* sid)
Radek Krejci469aab82012-07-22 18:42:20 +0200129{
Radek Krejcif23850c2012-07-23 16:14:17 +0200130 unsigned char hash_raw[APR_SHA1_DIGESTSIZE];
131 int i;
132
Radek Krejci469aab82012-07-22 18:42:20 +0200133 apr_sha1_ctx_t sha1_ctx;
134 apr_sha1_init(&sha1_ctx);
135 apr_sha1_update(&sha1_ctx, hostname, strlen(hostname));
136 apr_sha1_update(&sha1_ctx, port, strlen(port));
137 apr_sha1_update(&sha1_ctx, sid, strlen(sid));
Radek Krejcif23850c2012-07-23 16:14:17 +0200138 apr_sha1_final(hash_raw, &sha1_ctx);
Radek Krejci469aab82012-07-22 18:42:20 +0200139
Radek Krejcif23850c2012-07-23 16:14:17 +0200140 /* convert binary hash into hex string, which is printable */
141 *hash = malloc(sizeof(char) * ((2*APR_SHA1_DIGESTSIZE)+1));
142 for (i = 0; i < APR_SHA1_DIGESTSIZE; i++) {
143 snprintf(*hash + (2*i), 3, "%02x", hash_raw[i]);
144 }
145 //hash[2*APR_SHA1_DIGESTSIZE] = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200146
147 return (APR_SUCCESS);
148}
149
150int netconf_callback_ssh_hostkey_check (const char* hostname, int keytype, const char* fingerprint)
151{
152 /* always approve */
153 return (EXIT_SUCCESS);
154}
155
156char* netconf_callback_sshauth_password (const char* username, const char* hostname)
157{
158 char* buf;
159
160 buf = malloc ((strlen(password) + 1) * sizeof(char));
161 apr_cpystrn(buf, password, strlen(password) + 1);
162
163 return (buf);
164}
165
166void netconf_callback_sshauth_interactive (const char* name,
167 int name_len,
168 const char* instruction,
169 int instruction_len,
170 int num_prompts,
171 const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts,
172 LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses,
173 void** abstract)
174{
175 int i;
176
177 for (i=0; i<num_prompts; i++) {
178 responses[i].text = malloc ((strlen(password) + 1) * sizeof(char));
179 apr_cpystrn(responses[i].text, password, strlen(password) + 1);
180 responses[i].length = strlen(responses[i].text) + 1;
181 }
182
183 return;
184}
185
Radek Krejcif23850c2012-07-23 16:14:17 +0200186static void handle_msg_open(server_rec* server, apr_pool_t* pool, int client, apr_hash_t* conns, char** address)
Radek Krejci469aab82012-07-22 18:42:20 +0200187{
Radek Krejci469aab82012-07-22 18:42:20 +0200188 struct nc_session* session;
189 char *hostname, *port, *username, *sid;
Radek Krejcif23850c2012-07-23 16:14:17 +0200190 char *session_key;
191 char *reply;
Radek Krejci469aab82012-07-22 18:42:20 +0200192
193 hostname = address[0];
194 port = address[1];
195 username = address[2];
196
197 /* connect to the requested NETCONF server */
198 password = address[3];
199 session = nc_session_connect(hostname, (unsigned short) atoi (port), username, NULL);
200 password = NULL;
201
202 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "host: %s, port: %s, user: %s", hostname, port, username);
203
204 /* clear plaintext password */
205 size_t size = strlen(address[3]);
206 memset(address[3], 0, size + 1);
207
208 /* if connected successful, add session to the list */
209 if (session != NULL) {
210 /* generate hash for the session */
211 sid = nc_session_get_id(session);
Radek Krejcif23850c2012-07-23 16:14:17 +0200212 gserver = server;
213 gen_ncsession_hash(&session_key, hostname, port, sid);
Radek Krejci469aab82012-07-22 18:42:20 +0200214 free(sid);
Radek Krejcif23850c2012-07-23 16:14:17 +0200215 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "hash: %s", session_key);
Radek Krejci469aab82012-07-22 18:42:20 +0200216
Radek Krejcif23850c2012-07-23 16:14:17 +0200217 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Key in hash to add: %s", session_key);
218 apr_hash_set(conns, apr_pstrdup(pool, session_key), APR_HASH_KEY_STRING, (void *) session);
219 reply = malloc(strlen(session_key) + 2);
220 reply[0] = MSG_OK;
221 strcpy(&(reply[1]), session_key);
222 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "reply: %x%s", reply[0], &(reply[1]));
223 send(client, reply, strlen(session_key) + 2, 0);
Radek Krejci469aab82012-07-22 18:42:20 +0200224 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "NETCONF session established");
Radek Krejcif23850c2012-07-23 16:14:17 +0200225 free(session_key);
226 free(reply);
Radek Krejci469aab82012-07-22 18:42:20 +0200227 return;
228 } else {
229 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Connection could not be established");
230 }
231
Radek Krejcif23850c2012-07-23 16:14:17 +0200232 reply = malloc(2);
233 reply[0] = MSG_ERROR;
234 reply[1] = 0;
235 send(client, reply, 2, 0);
236 free(reply);
Radek Krejci469aab82012-07-22 18:42:20 +0200237}
238
239static void handle_msg_close(server_rec* server, int client, apr_hash_t* conns, char* session_key)
240{
241 struct nc_session *ns = NULL;
Radek Krejcif23850c2012-07-23 16:14:17 +0200242 char* reply;
Radek Krejci469aab82012-07-22 18:42:20 +0200243
Radek Krejcif23850c2012-07-23 16:14:17 +0200244 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Key in hash to get: %s", session_key);
Radek Krejci469aab82012-07-22 18:42:20 +0200245 ns = (struct nc_session *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
246 if (ns != NULL) {
247 nc_session_close (ns, "NETCONF session closed by client");
248 nc_session_free (ns);
249 ns = NULL;
250
251 /* remove session from the active sessions list */
252 apr_hash_set(conns, session_key, APR_HASH_KEY_STRING, NULL);
253 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "NETCONF session closed");
Radek Krejcif23850c2012-07-23 16:14:17 +0200254
255 reply = malloc(2);
256 reply[0] = MSG_OK;
257 reply[1] = 0;
258 send(client, reply, 2, 0);
259 free(reply);
Radek Krejci469aab82012-07-22 18:42:20 +0200260 } else {
261 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown session to close");
Radek Krejcif23850c2012-07-23 16:14:17 +0200262
263 reply = malloc(2);
264 reply[0] = MSG_ERROR;
265 reply[1] = 0;
266 send(client, reply, 2, 0);
267 free(reply);
Radek Krejci469aab82012-07-22 18:42:20 +0200268 }
269}
270
271static void handle_netconf_request(server_rec* server, int client, apr_hash_t* conns, char* session_key)
272{
273 struct nc_session *session = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +0200274 NC_DATASTORE target = NC_DATASTORE_RUNNING;
275 nc_rpc* rpc;
276 nc_reply* reply;
277 char* data, *client_reply_data;
278
279 session = (struct nc_session *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
280 if (session != NULL) {
281 /* create requests */
282 rpc = nc_rpc_getconfig (target, NULL);
283 if (rpc == NULL) {
284 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
285 goto error_reply;
286 }
287 /* send the request and get the reply */
288 nc_session_send_rpc (session, rpc);
289 if (nc_session_recv_reply (session, &reply) == 0) {
290 nc_rpc_free (rpc);
291 if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
292 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: receiving rpc-reply failed");
293 handle_msg_close(server, client, conns, session_key);
294 goto error_reply;
295 }
296 /* there is error handled by callback */
297 return;
298 }
299 nc_rpc_free (rpc);
300
301 switch (nc_reply_get_type (reply)) {
302 case NC_REPLY_DATA:
303 case NC_REPLY_ERROR:
304 data = nc_reply_dump(reply);
305 break;
306 default:
307 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected rpc-reply");
308 goto error_reply;
309 }
310 nc_reply_free(reply);
311
312 if (!data) {
313 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: no data from reply");
314 goto error_reply;
315 }
316
317 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "NETCONF get-config is done");
Radek Krejcif23850c2012-07-23 16:14:17 +0200318 client_reply_data = malloc(sizeof(char) * (1 + strlen(data) + 1));
Radek Krejci469aab82012-07-22 18:42:20 +0200319 if (client_reply_data == NULL) {
320 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: allocating memory for client reply failed");
321 goto error_reply;
322 }
323 client_reply_data[0] = MSG_DATA;
324 apr_cpystrn(&client_reply_data[1], data, strlen(data) + 1);
Radek Krejcif23850c2012-07-23 16:14:17 +0200325
326 client_reply_data[0] = MSG_OK;
327 send(client, client_reply_data, strlen(client_reply_data) + 2, 0);
328 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "sending message from daemon: %s", client_reply_data);
329 free(client_reply_data);
Radek Krejci469aab82012-07-22 18:42:20 +0200330 } else {
331 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown session to close");
332 goto error_reply;
333 }
334
335 return;
336
337error_reply:
Radek Krejcif23850c2012-07-23 16:14:17 +0200338 client_reply_data = malloc(2);
339 client_reply_data[0] = MSG_ERROR;
340 client_reply_data[1] = 0;
341 send(client, client_reply_data, 2, 0);
342 free(client_reply_data);
Radek Krejci469aab82012-07-22 18:42:20 +0200343}
344
345static void get_target_address(char *address[4], char *cred)
346{
347 int i;
348
349 /* <MSG_TYPE> <host> \0 <port> \0 <user> \0 <pass> \0 */
350
351 /* hostname */
352 address[0] = &cred[0];
353
354 /* process the rest in loop */
355 for (i = 1; i < 4; i++) {
356 /* port, user, password */
357 address[i] = address[i-1] + strlen(address[i-1]) + 1;
358 }
359}
360
361server_rec* clb_print_server;
362int clb_print(const char* msg)
363{
364 ap_log_error(APLOG_MARK, APLOG_INFO, 0, clb_print_server, msg);
365 return (0);
366}
367
Radek Krejcif23850c2012-07-23 16:14:17 +0200368/*
369 * This is actually implementation of NETCONF client
370 * - requests are received from UNIX socket in the predefined format
371 * - results are replied through the same way
372 * - the daemon run as a separate process, but it is started and stopped
373 * automatically by Apache.
374 *
375 */
Radek Krejci469aab82012-07-22 18:42:20 +0200376static void forked_proc(apr_pool_t * pool, server_rec * server)
377{
378 struct sockaddr_un local, remote;
379 int lsock, client;
380 socklen_t len, len2;
381 struct pollfd fds;
382 int status;
383
384 apr_hash_t *netconf_sessions_list;
385 char buffer[BUFFER_SIZE];
386 char *address[4];
387
Radek Krejcif23850c2012-07-23 16:14:17 +0200388 /* change uid and gid of process for security reasons */
Radek Krejci469aab82012-07-22 18:42:20 +0200389 unixd_setup_child();
390
Radek Krejcif23850c2012-07-23 16:14:17 +0200391 /* create listening UNIX socket to accept incoming connections */
Radek Krejci469aab82012-07-22 18:42:20 +0200392
393 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
394 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Creating socket failed (%s)", strerror(errno));
395 return;
396 }
397
398 local.sun_family = AF_UNIX;
399 strncpy(local.sun_path, SOCKET_FILENAME, sizeof(local.sun_path));
400 unlink(local.sun_path);
401 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
402
403 if (bind(lsock, (struct sockaddr *) &local, len) == -1) {
404 if (errno == EADDRINUSE) {
405 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "mod_netconf socket address already in use");
406 close(lsock);
407 exit(0);
408 }
409 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Binding socket failed (%s)", strerror(errno));
410 close(lsock);
411 return;
412 }
413
414 if (listen(lsock, MAX_SOCKET_CL) == -1) {
415 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Setting up listen socket failed (%s)", strerror(errno));
416 close(lsock);
417 return;
418 }
419
420 /* prepare internal lists */
421 netconf_sessions_list = apr_hash_make(pool);
422
423 /* setup libnetconf's callbacks */
424 nc_verbosity(NC_VERB_DEBUG);
425 clb_print_server = server;
426 nc_callback_print(clb_print);
427 nc_callback_ssh_host_authenticity_check(netconf_callback_ssh_hostkey_check);
428 nc_callback_sshauth_interactive(netconf_callback_sshauth_interactive);
429 nc_callback_sshauth_password(netconf_callback_sshauth_password);
430
431 /* disable publickey authentication */
432 nc_ssh_pref(NC_SSH_AUTH_PUBLIC_KEYS, -1);
433
434 while (isterminated == 0) {
435 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "waiting for another client's request");
436
437 /* open incoming connection if any */
438 len2 = sizeof(remote);
439 client = accept(lsock, (struct sockaddr *) &remote, &len2);
440 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
441 apr_sleep(SLEEP_TIME);
442 continue;
443 } else if (client == -1 && (errno == EINTR)) {
444 continue;
445 } else if (client == -1) {
446 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Accepting mod_netconf client connection failed (%s)", strerror(errno));
447 continue;
448 }
449 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "client's socket accepted.");
450
451 /* set client's socket as non-blocking */
Radek Krejcif23850c2012-07-23 16:14:17 +0200452 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +0200453
454 while (1) {
455 fds.fd = client;
456 fds.events = POLLIN;
457 fds.revents = 0;
458
459 status = poll(&fds, 1, 100);
460
461 if (status == -1 && errno == EINTR && isterminated == 0) {
462 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
463 continue;
464 } else if (status <= 0) {
465 /* 0: poll time outed
466 * close socket and ignore this request from the client, it can try it again
467 * -1: poll failed
468 * something wrong happend, close this socket and wait for another request
469 */
470 close(client);
471 break;
472 }
473 /* status > 0 */
474
475 /* check the status of the socket */
476
477 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
478 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
479 /* close client's socket (it's probably already closed by client */
480 close(client);
481 break;
482 }
483
484 if ((len2 = recv(client, buffer, BUFFER_SIZE, 0)) <= 0) {
485 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "receiving failed %d (%s)", errno, strerror(errno));
486 continue;
487 } else {
488 /* got message from client */
489 buffer[len2] = 0;
490 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "received from client: %s", buffer);
491
492 /* message type */
493 switch (buffer[0]) {
494 case MSG_OPEN:
495 get_target_address(address, &buffer[1]);
Radek Krejcif23850c2012-07-23 16:14:17 +0200496 handle_msg_open(server, pool, client, netconf_sessions_list, address);
Radek Krejci469aab82012-07-22 18:42:20 +0200497 break;
498 case MSG_DATA:
499 /* netconf data */
500 handle_netconf_request(server, client, netconf_sessions_list, &buffer[1]);
501 break;
502 case MSG_CLOSE:
503 handle_msg_close(server, client, netconf_sessions_list, &buffer[1]);
504 break;
505 default:
506 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown mod_netconf message type");
Radek Krejci469aab82012-07-22 18:42:20 +0200507 break;
508 }
509 }
510 }
511 }
512
513 close(lsock);
514
515 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Exiting from the mod_netconf daemon");
516
517 exit(APR_SUCCESS);
518}
519
Radek Krejcif23850c2012-07-23 16:14:17 +0200520static void *mod_netconf_create_conf(apr_pool_t * pool, server_rec * s)
Radek Krejci469aab82012-07-22 18:42:20 +0200521{
Radek Krejcif23850c2012-07-23 16:14:17 +0200522 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Init netconf module config");
523
524 mod_netconf_cfg *config = apr_pcalloc(pool, sizeof(mod_netconf_cfg));
525 apr_pool_create(&config->pool, pool);
526 config->forkproc = NULL;
527
528 return (void *)config;
Radek Krejci469aab82012-07-22 18:42:20 +0200529}
530
531static int mod_netconf_master_init(apr_pool_t * pconf, apr_pool_t * ptemp,
532 apr_pool_t * plog, server_rec * s)
533{
Radek Krejcif23850c2012-07-23 16:14:17 +0200534 mod_netconf_cfg *config;
535 apr_status_t res;
536
Radek Krejci469aab82012-07-22 18:42:20 +0200537 /* These two help ensure that we only init once. */
538 void *data;
Radek Krejcif23850c2012-07-23 16:14:17 +0200539 const char *userdata_key = "netconf_ipc_init";
Radek Krejci469aab82012-07-22 18:42:20 +0200540
541 /*
542 * The following checks if this routine has been called before.
543 * This is necessary because the parent process gets initialized
544 * a couple of times as the server starts up.
545 */
546 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
547 if (!data) {
548 apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
549 return (OK);
550 }
551
Radek Krejcif23850c2012-07-23 16:14:17 +0200552 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "creating mod_netconf daemon");
553 config = ap_get_module_config(s->module_config, &netconf_module);
Radek Krejcidfaa6ea2012-07-23 09:04:43 +0200554
Radek Krejcif23850c2012-07-23 16:14:17 +0200555 if (config && config->forkproc == NULL) {
556 config->forkproc = apr_pcalloc(config->pool, sizeof(apr_proc_t));
557 res = apr_proc_fork(config->forkproc, config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +0200558 switch (res) {
559 case APR_INCHILD:
560 /* set signal handler */
Radek Krejcif23850c2012-07-23 16:14:17 +0200561 apr_signal_init(config->pool);
Radek Krejci469aab82012-07-22 18:42:20 +0200562 apr_signal(SIGTERM, signal_handler);
563
564 /* log start of the separated NETCONF communication process */
Radek Krejcif23850c2012-07-23 16:14:17 +0200565 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "mod_netconf daemon started (PID %d)", getpid());
Radek Krejci469aab82012-07-22 18:42:20 +0200566
567 /* start main loop providing NETCONF communication */
Radek Krejcif23850c2012-07-23 16:14:17 +0200568 forked_proc(config->pool, s);
Radek Krejci469aab82012-07-22 18:42:20 +0200569
Radek Krejcif23850c2012-07-23 16:14:17 +0200570 /* I never should be here, wtf?!? */
571 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_netconf daemon unexpectedly stopped");
Radek Krejci469aab82012-07-22 18:42:20 +0200572 exit(APR_EGENERAL);
573 break;
574 case APR_INPARENT:
575 /* register child to be killed (SIGTERM) when the module config's pool dies */
Radek Krejcif23850c2012-07-23 16:14:17 +0200576 apr_pool_note_subprocess(config->pool, config->forkproc, APR_KILL_AFTER_TIMEOUT);
Radek Krejci469aab82012-07-22 18:42:20 +0200577 break;
578 default:
579 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "apr_proc_fork() failed");
580 break;
581 }
Radek Krejcif23850c2012-07-23 16:14:17 +0200582 } else {
583 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_netconf misses configuration structure");
Radek Krejci469aab82012-07-22 18:42:20 +0200584 }
585
586 return OK;
587}
588
589static int mod_netconf_fixups(request_rec *r)
590{
591 apr_pool_t *temp_pool = NULL;
592 char* operation;
Radek Krejcif23850c2012-07-23 16:14:17 +0200593 char *data;
Radek Krejci469aab82012-07-22 18:42:20 +0200594 int len;
595 int sock = -1;
596 char buffer[BUFFER_SIZE];
597 struct sockaddr_un addr;
Radek Krejcif23850c2012-07-23 16:14:17 +0200598 int retval = OK;
Radek Krejci469aab82012-07-22 18:42:20 +0200599
600 /* process only requests for the mod_netconf handler */
601 if (!r->handler || (strcmp(r->handler, "netconf") != 0)) {
602 return DECLINED;
603 }
604
605 /* allow running module only as subrequest */
606 if (r->main == NULL) {
607 return DECLINED;
608 }
609
610 /* create temporary pool */
611 apr_pool_create(&temp_pool, NULL);
612
613 operation = apr_pstrdup(temp_pool, apr_table_get(r->subprocess_env, "NETCONF_OP"));
614 if (operation == NULL) {
615 apr_pool_destroy(temp_pool);
616 return HTTP_INTERNAL_SERVER_ERROR;
617 }
618
619 /* connect to the daemon */
620 sock = socket(PF_UNIX, SOCK_STREAM, 0);
621 if (sock == -1) {
622 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Cannot create client's mod_netconf socket");
623 apr_pool_destroy(temp_pool);
624 return HTTP_INTERNAL_SERVER_ERROR;
625 }
626 addr.sun_family = AF_UNIX;
627 strncpy(addr.sun_path, SOCKET_FILENAME, sizeof(addr.sun_path));
628 len = strlen(addr.sun_path) + sizeof(addr.sun_family);
629 if (connect(sock, (struct sockaddr *) &addr, len) == -1) {
630 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Cannot connect to the mod_netconf daemon");
631 apr_pool_destroy(temp_pool);
632 return HTTP_INTERNAL_SERVER_ERROR;
633 }
634
635 /* process the request */
636 if (strcmp(operation, "connect") == 0) {
637 char *host = apr_pstrdup(r->pool, apr_table_get(r->subprocess_env, "NETCONF_HOST"));
638 char *port = apr_pstrdup(r->pool, apr_table_get(r->subprocess_env, "NETCONF_PORT"));
639 char *user = apr_pstrdup(r->pool, apr_table_get(r->subprocess_env, "NETCONF_USER"));
640 char *pass = apr_pstrdup(r->pool, apr_table_get(r->subprocess_env, "NETCONF_PASS"));
641
642 /* <MSG_TYPE><host>\0<port>\0<user>\0<pass>\0 */
Radek Krejcif23850c2012-07-23 16:14:17 +0200643 data = apr_psprintf(temp_pool, " %s %s %s %s", host, port, user, pass);
644 data[len = 0] = MSG_OPEN;
645 data[len += strlen(host) + 1] = 0; /* <host>\0 */
646 data[len += strlen(port) + 1] = 0; /* <port>\0 */
647 data[len += strlen(user) + 1] = 0; /* <user>\0 */
Radek Krejci469aab82012-07-22 18:42:20 +0200648 /* <pass>\0 is already done from printf, but we need to count index for send */
Radek Krejcif23850c2012-07-23 16:14:17 +0200649 data[len += strlen(pass) + 1] = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200650
Radek Krejcif23850c2012-07-23 16:14:17 +0200651 send(sock, data, len, 0);
Radek Krejci469aab82012-07-22 18:42:20 +0200652 len = recv(sock, buffer, BUFFER_SIZE, 0);
653
Radek Krejcif23850c2012-07-23 16:14:17 +0200654 if (len < 2) {
Radek Krejci469aab82012-07-22 18:42:20 +0200655 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Invalid reply from mod_netconf daemon");
656 apr_pool_destroy(temp_pool);
657 return HTTP_INTERNAL_SERVER_ERROR;
658 } else if (buffer[0] != MSG_OK) {
Radek Krejcif23850c2012-07-23 16:14:17 +0200659 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "received from daemon: %x%s", buffer[0], &(buffer[1]));
Radek Krejci469aab82012-07-22 18:42:20 +0200660 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Cannot connect with NETCONF server");
661 apr_pool_destroy(temp_pool);
662 return HTTP_INTERNAL_SERVER_ERROR;
663 } else {
664 /* ok, received OK message */
Radek Krejcif23850c2012-07-23 16:14:17 +0200665 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "received from daemon: %x%s", buffer[0], &(buffer[1]));
666 apr_table_set(r->main->subprocess_env, "NETCONF_NSID", &(buffer[1]));
Radek Krejci469aab82012-07-22 18:42:20 +0200667 }
668 } else if (strcmp(operation, "disconnect") == 0) {
Radek Krejcif23850c2012-07-23 16:14:17 +0200669 char* nsid = apr_pstrdup(r->pool, apr_table_get(r->subprocess_env, "NETCONF_NSID"));
670 data = apr_psprintf(temp_pool, " %s", nsid);
671 data[0] = MSG_CLOSE;
672 len = strlen(nsid) + 2;
673 send(sock, data, len, 0);
674 len = recv(sock, buffer, BUFFER_SIZE, 0);
675
676 if (len < 2) {
677 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Invalid reply from mod_netconf daemon");
678 retval = HTTP_INTERNAL_SERVER_ERROR;
679 goto cleanup;
680 } else if (buffer[0] != MSG_OK) {
681 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "received from daemon: %x%s", buffer[0], &(buffer[1]));
682 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Cannot connect with NETCONF server");
683 retval = HTTP_INTERNAL_SERVER_ERROR;
684 goto cleanup;
685 } else {
686 /* ok, received OK message */
687 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "received from daemon: %x%s", buffer[0], &(buffer[1]));
688 }
Radek Krejci469aab82012-07-22 18:42:20 +0200689
690 } else if (strcmp(operation, "get-config") == 0) {
691
692 } else {
Radek Krejcif23850c2012-07-23 16:14:17 +0200693 retval = HTTP_NOT_IMPLEMENTED;
694 goto cleanup;
Radek Krejci469aab82012-07-22 18:42:20 +0200695 }
696
Radek Krejcif23850c2012-07-23 16:14:17 +0200697cleanup:
Radek Krejci469aab82012-07-22 18:42:20 +0200698 if (sock != -1) {
699 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "client closes socket");
700 close (sock);
701 }
702 if (temp_pool != NULL) {
703 apr_pool_destroy(temp_pool);
704 }
705
Radek Krejcif23850c2012-07-23 16:14:17 +0200706 return retval;
Radek Krejci469aab82012-07-22 18:42:20 +0200707}
708
709/**
710 * Testing content handler
711 * This function should handle NETCONF and return UI
712 */
713static int mod_netconf_handler(request_rec * r)
714{
Radek Krejci469aab82012-07-22 18:42:20 +0200715 /* pseudo code of args to apr_proc_create() */
716 if (!r->handler || (strcmp(r->handler, "netconf") != 0)) {
717 return DECLINED;
718 }
719
Radek Krejcidfaa6ea2012-07-23 09:04:43 +0200720 /* everything was done in fixups */
Radek Krejci469aab82012-07-22 18:42:20 +0200721 return OK;
722}
723
724/**
725 * Register module hooks
726 */
727static void mod_netconf_register_hooks(apr_pool_t * p)
728{
Radek Krejcif23850c2012-07-23 16:14:17 +0200729 ap_hook_post_config(mod_netconf_master_init, NULL, NULL, APR_HOOK_LAST);
Radek Krejci469aab82012-07-22 18:42:20 +0200730 ap_hook_fixups(mod_netconf_fixups, NULL, NULL, APR_HOOK_FIRST);
731 ap_hook_handler(mod_netconf_handler, NULL, NULL, APR_HOOK_MIDDLE);
Radek Krejcif23850c2012-07-23 16:14:17 +0200732
Radek Krejci469aab82012-07-22 18:42:20 +0200733}
734
735/* Dispatch list for API hooks */
736module AP_MODULE_DECLARE_DATA netconf_module = {
737 STANDARD20_MODULE_STUFF,
738 NULL, /* create per-dir config structures */
739 NULL, /* merge per-dir config structures */
Radek Krejcif23850c2012-07-23 16:14:17 +0200740 mod_netconf_create_conf, /* create per-server config structures */
Radek Krejci469aab82012-07-22 18:42:20 +0200741 NULL, /* merge per-server config structures */
742 NULL, /* table of config file commands */
743 mod_netconf_register_hooks /* register hooks */
744};