server: handle pcre2 now returning -1 for "no match"
[rb-79.git] / rb79-ban-ip.c
blobb4a4d45c9158d2fd87fbdb46588c6d4a3e61a607
1 /*
2 * Copyright (c) 2017-2020, De Rais <derais@cock.li>
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
18 #include <errno.h>
19 #include <locale.h>
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <time.h>
24 #include <unistd.h>
26 #include <sodium.h>
28 #include "macros.h"
29 #include "rb79.h"
31 #include "config.h"
33 const char *program_name = "rb79-ban-ip";
35 /* Show usage message, do not exit */
36 void
37 usage(void)
39 size_t len = strlen(program_name);
41 printf("Usage: %s [ -i individual-ip ] # -i, -f mutually exclusive\n",
42 program_name);
43 printf(" %*s [ -f first-ip -l last-ip ]\n", (int) len, "");
44 printf(" %*s [ -m ban-message ]\n", (int) len, "");
45 printf(" %*s [ -b board-name ] # default: global ban\n",
46 (int) len, "");
47 printf(" %*s [ -u banned-until ] # default: 7 days from now\n",
48 (int) len, "");
51 /* Do the thing */
52 int
53 main(int argc, char **argv)
55 int ret = EINVAL;
56 struct configuration conf = { 0 };
57 int opt = 0;
58 const char *b_arg = 0;
59 size_t board_idx = 0;
60 uint_fast8_t global_ban = 0;
61 const char *first_ip = 0;
62 char *normalized_first_ip = 0;
63 const char *last_ip = 0;
64 char *normalized_last_ip = 0;
65 const char *ban_message = 0;
66 uint_fast8_t is_secret = 0;
67 const char *raw_until = 0;
68 time_t until = 0;
70 setlocale(LC_ALL, "");
72 /* Parse options */
73 while ((opt = getopt(argc, argv, "si:f:l:m:u:b:")) != -1) {
74 switch (opt) {
75 case 's':
76 is_secret = 1;
77 break;
78 case 'b':
80 if (b_arg) {
81 ERROR_MESSAGE("-b already specified");
82 goto done;
85 b_arg = optarg;
86 break;
87 case 'i':
89 if (first_ip) {
90 ERROR_MESSAGE("Only one IP address or"
91 " range may be specified");
92 goto done;
95 first_ip = optarg;
96 last_ip = optarg;
97 break;
98 case 'f':
100 if (first_ip) {
101 ERROR_MESSAGE("Only one IP address or"
102 " range may be specified");
103 goto done;
106 first_ip = optarg;
107 break;
108 case 'l':
110 if (last_ip) {
111 ERROR_MESSAGE("Only one IP address or"
112 " range may be specified");
113 goto done;
116 last_ip = optarg;
117 break;
118 case 'm':
120 if (ban_message) {
121 ERROR_MESSAGE("Only one ban message"
122 " may be specified");
123 goto done;
126 ban_message = optarg;
127 break;
128 case 'u':
130 if (raw_until) {
131 ERROR_MESSAGE("Only one expiry time"
132 " may be specified");
133 goto done;
136 raw_until = optarg;
137 break;
138 default:
139 usage();
140 goto done;
144 if (!first_ip ||
145 !last_ip) {
146 ERROR_MESSAGE("An IP address or range must be specified");
147 usage();
148 goto done;
151 if (!ban_message) {
152 ban_message = "";
155 if (raw_until) {
156 until = (time_t) strtoull(raw_until, 0, 0);
157 } else {
158 until = time(0) + (7 * 24 * 60 * 60);
161 conf = (struct configuration) {
162 /* */
163 .static_www_folder = static_www_folder, /* */
164 .work_path = work_path, /* */
165 .temp_dir_template = temp_dir_template, /* */
166 .trip_salt = trip_salt, /* */
167 .trip_salt_len = strlen(trip_salt), /* */
168 .boards = boards, /* */
169 .boards_num = NUM_OF(boards), /* */
170 .max_form_data_size = max_form_data_size, /* */
171 .max_file_size = max_file_size, /* */
172 .max_text_len = max_text_len, /* */
173 .filetypes = filetypes, /* */
174 .filetypes_num = NUM_OF(filetypes), /* */
175 .file_description_prog = file_description_prog, /* */
176 .headers = headers, /* */
177 .headers_num = NUM_OF(headers), /* */
178 .challenges = challenges, /* */
179 .challenges_num = NUM_OF(challenges), /* */
180 .wordfilter_inputs = wordfilter_inputs, /* */
181 .wordfilter_inputs_num = NUM_OF(wordfilter_inputs), /* */
182 .forbidden_inputs = forbidden_inputs, /* */
183 .forbidden_inputs_num = NUM_OF(forbidden_inputs), /* */
186 /* Interpret board */
187 if (!b_arg) {
188 global_ban = 1;
189 } else {
190 board_idx = (size_t) -1;
192 for (size_t j = 0; j < conf.boards_num; ++j) {
193 if (!strcmp(conf.boards[j].name, b_arg)) {
194 board_idx = j;
198 if (board_idx == (size_t) -1) {
199 ERROR_MESSAGE("No board \"%s\" known", b_arg);
200 goto done;
204 /* Interpret IP addresses */
205 if (util_normalize_ip(first_ip, &normalized_first_ip) < 0) {
206 goto done;
209 if (util_normalize_ip(last_ip, &normalized_last_ip) < 0) {
210 goto done;
213 /* Set up a minimal part of the system */
214 if (setup_locks(&conf) < 0) {
215 goto done;
218 if (setup_dbs(&conf) < 0) {
219 goto done;
222 if (setup_write_thread(&conf) < 0) {
223 goto done;
226 ret = db_insert_ban(global_ban, board_idx, normalized_first_ip,
227 normalized_last_ip, ban_message, is_secret, time(0),
228 until);
229 done:
230 clean_locks();
231 clean_dbs();
232 clean_write_thread();
233 free(normalized_first_ip);
234 free(normalized_last_ip);
236 return ret;