]>
Commit | Line | Data |
---|---|---|
1136f709 | 1 | /* Direct Query Server module for srvx 1.x |
2 | * Copyright 2006 Michael Poole <mdpoole@troilus.org> | |
3 | * | |
4 | * This file is part of srvx. | |
5 | * | |
6 | * srvx is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
be2c97a5 | 8 | * the Free Software Foundation; either version 3 of the License, or |
1136f709 | 9 | * (at your option) any later version. |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with srvx; if not, write to the Free Software Foundation, | |
18 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
19 | */ | |
20 | ||
21 | #include "conf.h" | |
22 | #include "hash.h" | |
23 | #include "ioset.h" | |
24 | #include "log.h" | |
25 | #include "modcmd.h" | |
26 | #include "proto.h" | |
27 | ||
28 | const char *qserver_module_deps[] = { NULL }; | |
29 | ||
30 | struct qserverClient { | |
31 | struct userNode *user; | |
32 | struct io_fd *fd; | |
33 | unsigned int id; | |
34 | unsigned int password_ok : 1; | |
35 | }; | |
36 | ||
37 | static struct log_type *qserver_log; | |
38 | static struct io_fd *qserver_listener; | |
39 | static struct qserverClient **qserver_clients; | |
40 | static dict_t qserver_dict; | |
41 | static unsigned int qserver_nbots; | |
42 | ||
43 | static struct { | |
44 | const char *password; | |
45 | } conf; | |
46 | ||
47 | static void | |
48 | qserver_privmsg(struct userNode *user, struct userNode *target, const char *text, UNUSED_ARG(int server_qualified)) | |
49 | { | |
50 | struct qserverClient *client; | |
51 | ||
52 | client = dict_find(qserver_dict, target->nick, NULL); | |
53 | assert(client->user == target); | |
54 | ioset_printf(client->fd, "%s P :%s\n", user->nick, text); | |
55 | } | |
56 | ||
57 | static void | |
58 | qserver_notice(struct userNode *user, struct userNode *target, const char *text, UNUSED_ARG(int server_qualified)) | |
59 | { | |
60 | struct qserverClient *client; | |
61 | ||
62 | client = dict_find(qserver_dict, target->nick, NULL); | |
63 | assert(client->user == target); | |
64 | ioset_printf(client->fd, "%s N :%s\n", user->nick, text); | |
65 | } | |
66 | ||
67 | static void | |
68 | qserver_readable(struct io_fd *fd) | |
69 | { | |
70 | struct qserverClient *client; | |
71 | struct service *service; | |
72 | char *argv[MAXNUMPARAMS]; | |
73 | unsigned int argc; | |
74 | size_t len; | |
75 | int res; | |
76 | char tmpline[MAXLEN]; | |
77 | ||
78 | client = fd->data; | |
79 | assert(client->fd == fd); | |
80 | res = ioset_line_read(fd, tmpline, sizeof(tmpline)); | |
81 | if (res < 0) | |
82 | return; | |
83 | else if (res == 0) { | |
84 | ioset_close(fd, 1); | |
85 | return; | |
86 | } | |
87 | len = strlen(tmpline); | |
88 | while (tmpline[len - 1] == '\r' || tmpline[len - 1] == '\n') | |
89 | tmpline[--len] = '\0'; | |
90 | argc = split_line(tmpline, false, ArrayLength(argv), argv); | |
91 | if (argc < 3) { | |
92 | ioset_printf(fd, "MISSING_ARGS\n"); | |
93 | return; | |
94 | } | |
95 | if (!strcmp(argv[1], "PASS") | |
96 | && conf.password | |
97 | && !strcmp(argv[2], conf.password)) { | |
98 | client->password_ok = 1; | |
99 | } else if ((client->password_ok || !conf.password) | |
100 | && (service = service_find(argv[1])) != NULL) { | |
101 | ioset_printf(fd, "%s S\n", argv[0]); | |
102 | svccmd_invoke_argv(client->user, service, NULL, argc - 2, argv + 2, 1); | |
103 | ioset_printf(fd, "%s E\n", argv[0]); | |
104 | } else { | |
105 | ioset_printf(fd, "%s X %s\n", argv[0], argv[1]); | |
106 | } | |
107 | } | |
108 | ||
109 | static void | |
110 | qserver_destroy_fd(struct io_fd *fd) | |
111 | { | |
112 | struct qserverClient *client; | |
113 | ||
114 | client = fd->data; | |
115 | assert(client->fd == fd); | |
116 | dict_remove(qserver_dict, client->user->nick); | |
117 | DelUser(client->user, NULL, 0, "client disconnected"); | |
118 | qserver_clients[client->id] = NULL; | |
119 | free(client); | |
120 | } | |
121 | ||
122 | static void | |
123 | qserver_accept(UNUSED_ARG(struct io_fd *listener), struct io_fd *fd) | |
124 | { | |
125 | struct qserverClient *client; | |
126 | struct sockaddr_storage ss; | |
127 | socklen_t sa_len; | |
128 | unsigned int ii; | |
129 | unsigned int jj; | |
130 | int res; | |
131 | char nick[NICKLEN+1]; | |
132 | char host[HOSTLEN+1]; | |
133 | char ip[HOSTLEN+1]; | |
134 | ||
135 | client = calloc(1, sizeof(*client)); | |
136 | fd->data = client; | |
137 | fd->line_reads = 1; | |
138 | fd->readable_cb = qserver_readable; | |
139 | fd->destroy_cb = qserver_destroy_fd; | |
140 | ||
141 | for (ii = 0; ii < qserver_nbots; ++ii) | |
142 | if (qserver_clients[ii] == NULL) | |
143 | break; | |
144 | if (ii == qserver_nbots) { | |
145 | qserver_nbots += 8; | |
146 | qserver_clients = realloc(qserver_clients, qserver_nbots * sizeof(qserver_clients[0])); | |
147 | for (jj = ii; jj < qserver_nbots; ++jj) | |
148 | qserver_clients[jj] = NULL; | |
149 | } | |
150 | client->id = ii; | |
151 | client->fd = fd; | |
152 | qserver_clients[client->id] = client; | |
153 | snprintf(nick, sizeof(nick), " QServ%04d", client->id); | |
154 | safestrncpy(host, "srvx.dummy.user", sizeof(host)); | |
155 | safestrncpy(ip, "0.0.0.0", sizeof(ip)); | |
156 | sa_len = sizeof(ss); | |
157 | res = getpeername(fd->fd, (struct sockaddr*)&ss, &sa_len); | |
158 | if (res == 0) { | |
159 | getnameinfo((struct sockaddr*)&ss, sa_len, ip, sizeof(host), NULL, 0, NI_NUMERICHOST); | |
160 | if (getnameinfo((struct sockaddr*)&ss, sa_len, host, sizeof(host), NULL, 0, 0) != 0) | |
161 | safestrncpy(host, ip, sizeof(host)); | |
162 | } | |
163 | client->user = AddLocalUser(nick, nick+1, host, "qserver dummy user", "*+oi"); | |
164 | irc_pton(&client->user->ip, NULL, ip); | |
165 | dict_insert(qserver_dict, client->user->nick, client); | |
166 | ||
167 | reg_privmsg_func(client->user, qserver_privmsg); | |
168 | reg_notice_func(client->user, qserver_notice); | |
169 | } | |
170 | ||
171 | static void | |
172 | qserver_conf_read(void) | |
173 | { | |
174 | struct addrinfo hints; | |
175 | struct addrinfo *ai; | |
176 | dict_t node; | |
177 | const char *str1; | |
178 | const char *str2; | |
179 | int res; | |
180 | ||
181 | ioset_close(qserver_listener, 1); | |
182 | qserver_listener = NULL; | |
183 | node = conf_get_data("modules/qserver", RECDB_OBJECT); | |
184 | if (!node) | |
185 | return; | |
186 | str1 = database_get_data(node, "bind_address", RECDB_QSTRING); | |
187 | if (!str1) | |
188 | str1 = database_get_data(node, "address", RECDB_QSTRING); | |
189 | str2 = database_get_data(node, "port", RECDB_QSTRING); | |
190 | if (!str2) | |
191 | return; | |
192 | memset(&hints, 0, sizeof(hints)); | |
193 | hints.ai_flags = AI_PASSIVE; | |
194 | hints.ai_socktype = SOCK_STREAM; | |
195 | res = getaddrinfo(str1, str2, &hints, &ai); | |
196 | if (res) { | |
197 | log_module(qserver_log, LOG_ERROR, "Unable to find address [%s]:%s: %s", str1 ? str1 : "", str2, gai_strerror(res)); | |
198 | } else if (!(qserver_listener = ioset_listen(ai->ai_addr, ai->ai_addrlen, NULL, qserver_accept))) { | |
199 | log_module(qserver_log, LOG_ERROR, "Unable to listen on [%s]:%s", str1 ? str1 : "", str2); | |
200 | } | |
201 | conf.password = database_get_data(node, "password", RECDB_QSTRING); | |
202 | freeaddrinfo(ai); | |
203 | } | |
204 | ||
205 | void | |
30874d66 | 206 | qserver_cleanup(UNUSED_ARG(void *extra)) |
1136f709 | 207 | { |
208 | unsigned int ii; | |
209 | ||
210 | ioset_close(qserver_listener, 1); | |
211 | for (ii = 0; ii < qserver_nbots; ++ii) | |
212 | if (qserver_clients[ii]) | |
213 | DelUser(qserver_clients[ii]->user, NULL, 0, "module finalizing"); | |
214 | dict_delete(qserver_dict); | |
215 | } | |
216 | ||
217 | int | |
218 | qserver_init(void) | |
219 | { | |
220 | qserver_log = log_register_type("QServer", "file:qserver.log"); | |
221 | conf_register_reload(qserver_conf_read); | |
222 | qserver_dict = dict_new(); | |
30874d66 | 223 | reg_exit_func(qserver_cleanup, NULL); |
1136f709 | 224 | return 1; |
225 | } | |
226 | ||
227 | int | |
228 | qserver_finalize(void) | |
229 | { | |
230 | return 1; | |
231 | } |