c++/domain-socket-bridge/domain-socket-bridge.cpp
author František Kučera <franta-hg@frantovo.cz>
Fri, 08 May 2020 00:02:05 +0200
changeset 57 1b21c78d8706
parent 43 b54d76467040
permissions -rw-r--r--
SQLite: demo modul – rozšíření přidávající pár funkcí do SQL
Vzniklo v rámci práce na článku: https://blog.frantovo.cz/c/383/Komplexita%3A%20%C5%99e%C5%A1en%C3%AD%20a%C2%A0prevence
     1 #include <string.h>
     2 #include <errno.h>
     3 #include <stdio.h>
     4 #include <signal.h>
     5 #include <stdlib.h>
     6 #include <string>
     7 #include <regex>
     8 
     9 #include <event2/bufferevent.h>
    10 #include <event2/buffer.h>
    11 #include <event2/listener.h>
    12 #include <event2/util.h>
    13 #include <event2/event.h>
    14 #include <sys/socket.h>
    15 #include <sys/un.h>
    16 #include <unistd.h>
    17 
    18 using namespace std;
    19 
    20 static const string HELLO_MESSAGE("Hello, World!\n");
    21 static const string SOCKET_PATH("./roura");
    22 static const string COMMAND_EXIT("exit\n");
    23 
    24 static void listener_cb(evutil_socket_t, short, void *);
    25 static void conn_read_cb(struct bufferevent *, void *);
    26 static void conn_write_cb(struct bufferevent *, void *);
    27 static void conn_event_cb(struct bufferevent *, short, void *);
    28 static void signal_cb(evutil_socket_t, short, void *);
    29 static void print_socket_info(int, int);
    30 
    31 int main(int argc, char **argv) {
    32 	printf("%4s %8s\n", "*", "STARTED");
    33 	struct event_base *base;
    34 	evutil_socket_t listener;
    35 	struct event *listener_event;
    36 	struct event *signal_event;
    37 
    38 	struct sockaddr_un sun;
    39 
    40 	setvbuf(stdout, NULL, _IONBF, 0);
    41 
    42 	base = event_base_new();
    43 	if (!base) {
    44 		printf("%4s %8s %s\n", "*", "ERROR", "unable to initialize libevent");
    45 		return 1;
    46 	}
    47 
    48 	memset(&sun, 0, sizeof (sun));
    49 	sun.sun_family = AF_UNIX;
    50 	strcpy(sun.sun_path, SOCKET_PATH.c_str());
    51 
    52 	listener = socket(AF_UNIX, SOCK_STREAM, 0);
    53 	evutil_make_socket_nonblocking(listener);
    54 
    55 	if (bind(listener, (struct sockaddr*) &sun, sizeof (sun)) < 0) {
    56 		printf("%4s %8s %s: %s\n", "*", "ERROR", "unable to create domain socket:", SOCKET_PATH.c_str());
    57 		return 1;
    58 	}
    59 
    60 	if (listen(listener, 16) < 0) {
    61 		printf("%4s %8s %s\n", "*", "ERROR", "unable to listen");
    62 		return 1;
    63 	}
    64 
    65 	// identifikátor serverového soketu (v současnosti číslo FD)
    66 	printf("%4s %8s listening at: sun_path = %s → socketId = %d\n", "*", "SOCKET", sun.sun_path, listener);
    67 
    68 	listener_event = event_new(base, listener, EV_READ | EV_PERSIST, listener_cb, (void*) base);
    69 
    70 	if (!listener_event) {
    71 		printf("%4s %8s %s\n", "*", "ERROR", "unable to do event_new()");
    72 		return 1;
    73 	}
    74 
    75 	event_add(listener_event, NULL);
    76 
    77 	signal_event = evsignal_new(base, SIGINT, signal_cb, (void *) base);
    78 
    79 	if (!signal_event || event_add(signal_event, NULL) < 0) {
    80 		printf("%4s %8s %s\n", "*", "ERROR", "unable to create/add a signal event");
    81 		return 1;
    82 	}
    83 
    84 	event_base_dispatch(base);
    85 
    86 	event_free(listener_event);
    87 	event_free(signal_event);
    88 	event_base_free(base);
    89 
    90 	// smažeme soket na disku / soubor -- jinak by program příště spadl na bind()
    91 	// TODO: co když soket někdo přesune a místo něj dá jiný soubor?
    92 	unlink(SOCKET_PATH.c_str());
    93 
    94 	printf("%4s %8s\n", "*", "FINISHED");
    95 	return 0;
    96 }
    97 
    98 static void listener_cb(evutil_socket_t listener, short event, void *user_data) {
    99 	struct event_base *base = (event_base *) user_data;
   100 	struct bufferevent *bev;
   101 
   102 	struct sockaddr_storage ss;
   103 	socklen_t slen = sizeof (ss);
   104 	int fd = accept(listener, (struct sockaddr*) &ss, &slen);
   105 
   106 	// identifikátor navázaného spojení (v současnosti číslo FD)
   107 	int * connectionId = (int*) malloc(sizeof (fd));
   108 	*connectionId = fd;
   109 
   110 	if (fd < 0) {
   111 		printf("%4d %8s %s\n", *connectionId, "ERROR", "unable to accept()");
   112 		return;
   113 	} else if (fd > FD_SETSIZE) {
   114 		// FD_SETSIZE = 1024 -- Proč? Co když bude spojení víc?
   115 		printf("%4d %8s fd (%d) > FD_SETSIZE (%d)\n", *connectionId, "ERROR", fd, FD_SETSIZE);
   116 		close(fd);
   117 		return;
   118 	}
   119 
   120 	evutil_make_socket_nonblocking(fd);
   121 
   122 
   123 	bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
   124 	if (!bev) {
   125 		printf("%4d %8s %s\n", *connectionId, "ERROR", "constructing bufferevent");
   126 		event_base_loopbreak(base);
   127 		return;
   128 	}
   129 
   130 
   131 
   132 
   133 	bufferevent_setcb(bev, conn_read_cb, conn_write_cb, conn_event_cb, (void*) connectionId);
   134 	bufferevent_enable(bev, EV_READ | EV_WRITE);
   135 
   136 	printf("%4d %8s somebody has connected: socketId = %d → connectionId = %d\n", *connectionId, "CONN", listener, *connectionId);
   137 	print_socket_info(*connectionId, fd);
   138 
   139 	bufferevent_write(bev, HELLO_MESSAGE.c_str(), strlen(HELLO_MESSAGE.c_str()));
   140 }
   141 
   142 static void conn_read_cb(struct bufferevent *bev, void *user_data) {
   143 	int connectionId = *((int*) user_data);
   144 
   145 	/* This callback is invoked when there is data to read on bev. */
   146 	struct evbuffer *input = bufferevent_get_input(bev);
   147 	struct evbuffer *output = bufferevent_get_output(bev);
   148 	struct event_base *base = bufferevent_get_base(bev);
   149 
   150 	size_t len = evbuffer_get_length(input);
   151 	char *data = (char*) malloc(len + 1);
   152 	data[len] = 0; // as a string must be terminated by \0 
   153 	evbuffer_copyout(input, data, len);
   154 
   155 	//TODO: there might already be \0 in the byte stream, only the part before the first \0 will be formatted as a string
   156 	string dataFormated(data);
   157 	dataFormated = regex_replace(dataFormated, regex("\\n"), "\\n");
   158 	printf("%4d %8s '%s'\n", connectionId, "IN", dataFormated.c_str());
   159 
   160 	if (COMMAND_EXIT.compare(data) == 0) {
   161 		struct timeval delay = {2, 123};
   162 		printf("%4d %8s client asks us to terminate; finishing in %ld sesonds and %ld microseconds\n", connectionId, "EXIT", delay.tv_sec, delay.tv_usec);
   163 		event_base_loopexit(base, &delay);
   164 	}
   165 
   166 	evbuffer_add(output, "echo: ", 6);
   167 	/* Copy all the data from the input buffer to the output buffer. */
   168 	evbuffer_add_buffer(output, input);
   169 	free(data);
   170 }
   171 
   172 static void conn_write_cb(struct bufferevent *bev, void *user_data) {
   173 	int connectionId = *((int*) user_data);
   174 
   175 	struct evbuffer *output = bufferevent_get_output(bev);
   176 	if (evbuffer_get_length(output) == 0) {
   177 		printf("%4d %8s\n", connectionId, "FLUSH");
   178 		/* nebudeme ukončovat spojení
   179 		bufferevent_free(bev);
   180 		 */
   181 	} else {
   182 		// FIXME: sem to nikdy nepřijde
   183 		size_t len = evbuffer_get_length(output);
   184 		char *data = (char*) malloc(len);
   185 		evbuffer_copyout(output, data, len);
   186 		printf("%4d %8s '%s'\n", connectionId, "OUT", data);
   187 		free(data);
   188 	}
   189 }
   190 
   191 static void conn_event_cb(struct bufferevent *bev, short events, void *user_data) {
   192 	int connectionId = *((int*) user_data);
   193 
   194 	if (events & BEV_EVENT_EOF) {
   195 		printf("%4d %8s\n", connectionId, "CLOSE");
   196 	} else if (events & BEV_EVENT_ERROR) {
   197 		printf("%4d %8s %s\n", connectionId, "ERROR", strerror(errno));
   198 	} else {
   199 		printf("%4d %8s\n", connectionId, "OTHER");
   200 		// None of the other events can happen here, since we haven't enabled timeouts
   201 	}
   202 
   203 	bufferevent_free(bev);
   204 	free(user_data);
   205 }
   206 
   207 static void signal_cb(evutil_socket_t sig, short events, void *user_data) {
   208 	struct event_base *base = (event_base *) user_data;
   209 	struct timeval delay = {2, 123};
   210 
   211 	printf("\n%4s %8s got SIGINT (Ctrl+C); finishing in %ld sesonds and %ld microseconds\n", "*", "SIGNAL", delay.tv_sec, delay.tv_usec);
   212 
   213 	event_base_loopexit(base, &delay);
   214 }
   215 
   216 static void print_socket_info(int connectionId, int fd) {
   217 	struct ucred cr;
   218 	unsigned int cl = sizeof (cr);
   219 
   220 	if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl) == 0) {
   221 		printf("%4d %8s client identification: pid=%d, uid=%d, gid=%d\n", connectionId, "CONN", cr.pid, cr.uid, cr.gid);
   222 	}
   223 }