💾 Archived View for gemini.rmf-dev.com › repo › Vaati › RouterMonitor › files › db55d9ed8e73a10bfa3e… captured on 2022-07-16 at 17:11:57. Gemini links have been rewritten to link to archived content

View Raw

More Information

-=-=-=-=-=-=-

0 #include <stdio.h>

1 #include <stdlib.h>

2 #include <stdarg.h>

3 #include <string.h>

4 #include <unistd.h>

5 #include <sys/types.h>

6 #include <sys/sysctl.h>

7 #include <sys/socket.h>

8 #include <ifaddrs.h>

9 #include <netdb.h>

10 #include <net/if.h>

11 #include <signal.h>

12 #include <time.h>

13 #include <sys/stat.h>

14 #include <sys/wait.h>

15 #include <pwd.h>

16 #include <errno.h>

17 #include <crypto/rmd160.h>

18

19 // PF

20 #include <sys/socket.h>

21 #include <sys/ioctl.h>

22 #include <net/if.h>

23 #include <net/pfvar.h>

24 #include <fcntl.h>

25

26 #include "config.h"

27 #define GEMINI_PART 9

28 #define USER "_rtmonitor"

29 #define LEASES "/var/db/dhcpd.leases"

30

31 #include <err.h>

32 #include <syslog.h>

33

34 void status(const int code, const char *file_mime)

35 {

36 printf("%!i(MISSING) %!s(MISSING)\r\n", code, file_mime);

37 fflush(stdout);

38 }

39

40 void errlogn(const char *format, ...) {

41 status(50, "FATAL ERROR");

42

43 char e[1024] = {'\0'};

44 va_list ap;

45

46 va_start(ap, format);

47 vsnprintf(e, sizeof(e), format, ap);

48 va_end(ap);

49

50 syslog(LOG_DAEMON, "%!s(MISSING)", e);

51 exit(2);

52 }

53

54 void errlog(const char *format, ...)

55 {

56 char e[1024] = {'\0'};

57 va_list ap;

58

59 va_start(ap, format);

60 vsnprintf(e, sizeof(e), format, ap);

61 va_end(ap);

62

63 syslog(LOG_DAEMON, "%!s(MISSING)", e);

64 exit(1);

65 }

66

67 const char* index_template =

68 "# OpenBSD Router\n\n"

69 "## System Info\n\n"

70 "* OS: %!s(MISSING) %!s(MISSING)\n"

71 "* CPU: %!s(MISSING) (%!d(MISSING))\n"

72 "* Uptime: %!s(MISSING)\n"

73 "* Memory: %!s(MISSING)\n\n"

74 "## Network Info\n\n"

75 "%!s(MISSING)"

76 "## DHCP Leases\n\n"

77 "%!s(MISSING)"

78 "## IP Blacklist\n"

79 "%!s(MISSING)\n";

80

81 const char* if_template =

82 "%!s(MISSING)"

83 "### %!s(MISSING)\n"

84 "\tIP: %!s(MISSING)\n"

85 "\tBytes: %!f(MISSING) GB\n"

86 "\tPackets: %!l(MISSING)ld\n\n";

87

88 const char* dhcp_template =

89 "%!s(MISSING)"

90 "### %!s(MISSING)\n"

91 "\tIP: %!s(MISSING)\n"

92 "\tStart: %!s(MISSING) %!s(MISSING)\n"

93 "\tEnd: %!s(MISSING) %!s(MISSING)\n\n";

94

95 const char* block_template =

96 "%!s(MISSING)"

97 "* %!s(MISSING)\n";

98

99

100 int pid[2] = {-1, -1};

101

102 int dev_pf = -1;

103

104 void pfinit() {

105 dev_pf = open("/dev/pf", O_RDWR);

106 if (dev_pf < 0)

107 errlog("Failed open pf\n");

108 struct pf_status status;

109 if (ioctl(dev_pf, DIOCGETSTATUS, &status) == -1)

110 errlog("Failed to retrieve pf status\n");

111 if (!status.running)

112 errlog("PF is disabled\n");

113 }

114

115 struct pfr_addr* getpfaddrs(char* table, struct pfr_addr* addr, size_t* len) {

116 if (dev_pf == -1)

117 errlog("File description uninitialized\n");

118

119 struct pfioc_table io;

120 struct pfr_table tbl;

121

122 explicit_bzero(&io, sizeof(io));

123 explicit_bzero(&tbl, sizeof(tbl));

124 strlcpy(tbl.pfrt_name, table, sizeof(tbl.pfrt_name));

125 io.pfrio_table = tbl;

126 io.pfrio_buffer = addr;

127 io.pfrio_esize = sizeof(*addr);

128 io.pfrio_size = *len;

129 if (ioctl(dev_pf, DIOCRGETADDRS, &io))

130 errlog("Failed to fetch addresses from pf table\n");

131 if (io.pfrio_size > *len)

132 return NULL;

133 *len = io.pfrio_size;

134 return addr;

135 }

136

137 struct if_info {

138 unsigned long long bytes;

139 unsigned long long packets;

140 char name[64];

141 char host[NI_MAXHOST];

142 };

143

144 struct if_info ifinfo[MAX_INTERFACES];

145 struct if_info *getifinfo(char* name) {

146 for (int i=0; i<MAX_INTERFACES; i++) {

147 if (ifinfo[i].name[0] == '\0') {

148 strncpy(ifinfo[i].name, name, 64);

149 return &ifinfo[i];

150 }

151 if (strncmp(ifinfo[i].name, name, sizeof(ifinfo[i].name)) == 0)

152 return &ifinfo[i];

153 }

154 return NULL;

155 }

156

157 void fetchif(int fd) {

158 explicit_bzero(ifinfo, sizeof(ifinfo));

159 struct ifaddrs *ifap;

160 if (getifaddrs(&ifap)) {

161 syslog(LOG_DAEMON, "Failed to fetch network interfaces(%!d(MISSING))\n", errno);

162 return;

163 }

164 for (struct ifaddrs* ifp = ifap; ifp; ifp=ifp->ifa_next) {

165 if (strlen(ifp->ifa_name)>1 && ifp->ifa_name[0] == 'l' && ifp->ifa_name[1] == 'o')

166 continue;

167 int family = ifp->ifa_addr->sa_family;

168 if (family == AF_LINK && ifp->ifa_data != NULL) {

169 struct if_data *stats = ifp->ifa_data;

170 if (stats->ifi_ipackets == 0 && stats->ifi_ibytes == 0)

171 continue;

172 struct if_info *info = getifinfo(ifp->ifa_name);

173 if (!info) {

174 syslog(LOG_DAEMON, "Too many network interface (above %!d(MISSING))\n", MAX_INTERFACES);

175 break;

176 }

177 info->bytes = stats->ifi_ibytes;

178 info->packets = stats->ifi_ipackets;

179 continue;

180 }

181 if (family != AF_INET && family != AF_INET6)

182 continue;

183 char hbuf[NI_MAXHOST];

184 if (getnameinfo(ifp->ifa_addr, sizeof(*(ifp->ifa_addr)), hbuf, sizeof(hbuf), NULL,

185 0, NI_NUMERICHOST) == 0) {

186 struct if_info *info = getifinfo(ifp->ifa_name);

187 if (!info) {

188 syslog(LOG_DAEMON, "Too many network interface (above %!d(MISSING))\n", MAX_INTERFACES);

189 break;

190 }

191 strncpy(info->host, hbuf, NI_MAXHOST);

192 }

193 }

194 freeifaddrs(ifap);

195 char ifbuf[4096];

196 ifbuf[0] = '\0';

197 for (int i=0; i<MAX_INTERFACES && ifinfo[i].name[0]; i++) {

198 snprintf(ifbuf, sizeof(ifbuf), if_template, ifbuf,

199 ifinfo[i].name, ifinfo[i].host, (float)ifinfo[i].bytes/1000000000, ifinfo[i].packets);

200 }

201 write(fd, ifbuf, strnlen(ifbuf, sizeof(ifbuf)));

202 }

203

204 int getuptime(char* out, size_t len) {

205 struct timespec boottime;

206 if (clock_gettime(CLOCK_BOOTTIME, &boottime) == -1)

207 return -1;

208 time_t uptime = boottime.tv_sec;

209 if (uptime > 59) {

210 uptime += 30;

211 int days = uptime / (3600*24);

212 uptime %!=(MISSING) (3600*24);

213 int hrs = uptime / 3600;

214 uptime %!=(MISSING) 3600;

215 int mins = uptime / 60;

216 memset(out, '\0', len);

217 if (days > 0)

218 snprintf(out, len, "%!d(MISSING) day%!s(MISSING), ", days, days > 1 ? "s" : "");

219 if (hrs > 0 && mins > 0)

220 snprintf(out, len, "%!s(MISSING)%!d(MISSING) hour%!s(MISSING), %!d(MISSING) min%!s(MISSING)", out, hrs, hrs > 1 ? "s" : "", mins, mins > 1 ? "s" : "");

221 else {

222 if (hrs > 0)

223 snprintf(out, len, "%!s(MISSING)%!d(MISSING) hour%!s(MISSING)", out, hrs, hrs > 1 ? "s" : "");

224 if (mins > 0 || (days == 0 && hrs == 0))

225 snprintf(out, len, "%!s(MISSING)%!d(MISSING) min%!s(MISSING)", out, mins, mins > 1 ? "s" : "");

226 }

227 } else {

228 snprintf(out, len, "%!l(MISSING)ld", uptime);

229 }

230 return 0;

231 }

232

233 struct lease {

234 char ip[32];

235 char hostname[256];

236 char date[32];

237 char time[32];

238 char date_end[32];

239 char time_end[32];

240 };

241

242 int readleases(struct lease* leases, int len) {

243 FILE* f = fopen(LEASES, "r");

244 if (!f)

245 return -1;

246 char* line = NULL;

247 size_t length = 0;

248 ssize_t read;

249

250 int i = 0;

251 int j = 0;

252 int hostname_found = 0;

253 while ((read = getline(&line, &length, f) != -1)) {

254 if (strstr(line, "lease") && strchr(line, '{') && strstr(line, "lease") < strchr(line, '{')) {

255 char* ip = strchr(line, ' ')+1;

256 if (!ip)

257 goto failed;

258 char* ptr = strchr(ip, ' ');

259 if (!ptr)

260 goto failed;

261 ptr[0] = '\0';

262 int exist = 0;

263 for (int k=0; k<i; k++) {

264 if (strcmp(ip, leases[k].ip) == 0) {

265 exist = 1;

266 break;

267 }

268 }

269 if (!exist) {

270 strlcpy(leases[i].ip, ip, sizeof(leases[i].ip));

271 j = 1;

272 }

273 } else if (strstr(line, "client-hostname")) {

274 char* hostname = strchr(line, '"')+1;

275 if (!hostname)

276 goto failed;

277 strchr(hostname, '"')[0] = '\0';

278 strlcpy(leases[i].hostname, hostname, sizeof(leases[i].hostname));

279 j++;

280 hostname_found = 1;

281 } else if (strstr(line, "start") || strstr(line, "end")) {

282 int start = 1;

283 char* ptr = strstr(line, "start");

284 if (!ptr) {

285 ptr = strstr(line, "end");

286 start = 0;

287 }

288 int word = 0;

289 int inspace = 0;

290 char* date = NULL;

291 char* time = NULL;

292 for (; *ptr&&*ptr!='\n'; ptr++) {

293 if (*ptr == ' ' || *ptr == '\t')

294 inspace = 1;

295 else if (inspace) {

296 inspace = 0;

297 word++;

298 if (word==2) date = ptr;

299 if (word==3) {

300 time = ptr;

301 break;

302 }

303 }

304 }

305 if (word!=3) goto failed;

306 char* space = strchr(date, ' ');

307 if (!space) goto failed;

308 *space = 0;

309 space = strchr(time, ' ');

310 if (!space) goto failed;

311 *space = 0;

312 strlcpy(start?leases[i].date:leases[i].date_end, date, sizeof(leases[i].date));

313 strlcpy(start?leases[i].time:leases[i].time_end, time, sizeof(leases[i].time));

314 j++;

315 } else if (strchr(line, '}')) {

316 if (!hostname_found) {

317 strlcpy(leases[i].hostname, "Unknown", sizeof(leases[i].hostname));

318 j++;

319 }

320 hostname_found = 0;

321 if (j>3) {

322 time_t t = time(NULL);

323 struct tm tm = *gmtime(&t);

324 int date[3];

325 int time[3];

326 int len, pos, last;

327 last = pos = 0;

328 len = strnlen(leases[i].date_end, 32);

329 char buf[32];

330 strlcpy(buf, leases[i].date_end, 32);

331 for (int i=0; i<32; i++) {

332 int end = 0;

333 if (buf[i] == '\0') end = 1;

334 if (buf[i] == '/' || end) {

335 buf[i] = '\0';

336 date[pos] = strtol(&buf[last], NULL, 10);

337 if (end) break;

338 pos++;

339 last = i+1;

340 }

341 }

342 date[0] -= 1900;

343 date[1] -= 1;

344 time[1] -= 1;

345 strlcpy(buf, leases[i].time_end, 32);

346 pos = last = 0;

347 for (int i=0; i<32; i++) {

348 int end = 0;

349 if (buf[i] == '\0') end = 1;

350 if (buf[i] == ':' || end) {

351 buf[i] = '\0';

352 time[pos] = strtol(&buf[last], NULL, 10);

353 if (end) break;

354 pos++;

355 last = i+1;

356 }

357 }

358 j = 0;

359 if (tm.tm_year>date[0]) continue;

360 if (tm.tm_year<date[0]) { i++; continue; }

361 if (tm.tm_mon>date[1]) continue;

362 if (tm.tm_mon<date[1]) { i++; continue; }

363 if (tm.tm_mday>date[2]) continue;

364 if (tm.tm_mday<date[2]) { i++; continue; }

365 if (tm.tm_hour>time[0]) continue;

366 if (tm.tm_hour<time[0]) { i++; continue; }

367 if (tm.tm_min>time[1]) continue;

368 if (tm.tm_min<time[1]) { i++; continue; }

369 if (tm.tm_sec>time[2]) continue;

370 if (tm.tm_sec<time[2]) { i++; continue; }

371 }

372 j = 0;

373 }

374 }

375 free(line);

376 return i;

377 failed:

378 free(line);

379 syslog(LOG_DAEMON, "Failed to parse leases file, %!s(MISSING)\n", LEASES);

380 return -1;

381 }

382

383 void ipv4str(unsigned int addr, char* buf, unsigned int len) {

384 uint8_t *b = (uint8_t*)&addr;

385 snprintf(buf, len, "%!d(MISSING).%!d(MISSING).%!d(MISSING).%!d(MISSING)", b[0], b[1], b[2], b[3]);

386 }

387

388 void fetchaddrs(int fd) {

389 struct pfr_addr addrs[ADDRS];

390 size_t length = ADDRS;

391 struct pfr_addr* addrs_ptr = getpfaddrs(TABLE, addrs, &length);

392 char list[4096];

393 list[0] = '\0';

394

395 for (int i=0; addrs_ptr && i<length; i++) {

396 if (strnlen(list, 4096) > sizeof(list)-32) break;

397 char ip[32];

398 ipv4str(addrs_ptr[i].pfra_u._pfra_ip4addr.s_addr, ip, sizeof(ip));

399 snprintf(list, sizeof(list), block_template, list, ip);

400 }

401 write(fd, list, strnlen(list, sizeof(list)));

402 }

403

404 int main(int argc, char* argv[]) {

405

406 struct passwd *p;

407 if ((p = getpwnam(USER)) == NULL)

408 errlog("Failed to fetch uid\n");

409

410 // Open pf, require root

411 pfinit();

412

413 // Only needs to read leases file

414 if (unveil(LEASES, "r") || unveil(NULL, NULL))

415 errlog("Failed to unveil\n");

416

417 // Drop privileges

418 if (setgroups(0, NULL))

419 errlog("Failed to set process groups\n");

420 if (setgid(p->pw_gid))

421 errlog("Failed to set process gid\n");

422 if (setuid(p->pw_uid))

423 errlog("Failed to set process uid\n");

424

425 // Read request from stdin

426 char request[1024];

427 if (fgets(request, sizeof(request), stdin) == NULL) {

428 if (feof(stdin)) {

429 status(59, "request is too short and probably empty");

430 errlogn("request is too short and probably empty");

431 } else if (ferror(stdin)) {

432 status(59, "error while reading request");

433 errlogn("error while reading request: %!s(MISSING)", request);

434 }

435 }

436

437 // Check if request is complete

438 if (request[strnlen(request, sizeof(request)) - 1] != '\n') {

439 status(59, "request is too long (1024 max)");

440 errlogn("request is too long (1024 max): %!s(MISSING)", request);

441 }

442

443 // Remove \r character

444 char* pos = strchr(request, '\r');

445 if (pos != NULL)

446 *pos = '\0';

447

448 // Check if gemini:// is there

449 if (strncmp(request, "gemini://", GEMINI_PART) != 0)

450 errlogn("request %!s(MISSING) doesn't match gemini://", request);

451

452 // Hide password in the log

453 pos = strchr(request, '?');

454 if (pos)

455 *pos = '\0';

456 syslog(LOG_DAEMON, "request: %!s(MISSING)\n", request);

457 if (pos)

458 *pos = '?';

459

460 char* hostname = request+GEMINI_PART;

461 pos = strchr(hostname, '/');

462 if (pos != NULL) {

463 pos[0] = '\0';

464 pos++;

465 }

466

467 // Check if hostname is the right one

468 if (strcmp(hostname, HOSTNAME)) {

469 status(59, "invalid hostname");

470 errlogn("wrong hostname expecting %!s(MISSING), got %!s(MISSING)", HOSTNAME, hostname);

471 }

472

473 if (!pos || *pos != '?') {

474 // Ask for password

475 status(11, "Password");

476 return 0;

477 } else {

478 // Check password

479 pos++;

480 uint32_t hash[5];

481 uint32_t password[5] = {PASSWORD_0,

482 PASSWORD_1,

483 PASSWORD_2,

484 PASSWORD_3,

485 PASSWORD_4

486 };

487 RMD160_CTX rmd160;

488 RMD160Init(&rmd160);

489 RMD160Update(&rmd160, pos, strlen(pos));

490 RMD160Final((unsigned char*)hash, &rmd160);

491 if (memcmp(hash, password, sizeof(hash))) {

492 status(59, "invalid password");

493 return 0;

494 }

495 }

496

497 // Write header

498 status(20, "text/gemini");

499

500 // Hardware info

501 int mib[2];

502 size_t len;

503 mib[0] = CTL_HW;

504 // CPU Name

505 mib[1] = HW_MODEL;

506 char cpuname[64];

507 len = sizeof(cpuname);

508 if (sysctl(mib, 2, cpuname, &len, NULL, 0) == -1)

509 errlog("sysctl failed\n");

510 // Physical memory

511 mib[1] = HW_PHYSMEM;

512 unsigned int pmem;

513 memset(&pmem, 0, sizeof(pmem));

514 len = sizeof(pmem);

515 if (sysctl(mib, 2, &pmem, &len, NULL, 0) == -1)

516 errlog("sysctl failed\n");

517

518 // Reading addresses

519 int pfpipe[2];

520 pipe(pfpipe);

521 pid[0] = fork();

522 if (pid[0] == 0) {

523 // No pledge allows to do that

524 fetchaddrs(pfpipe[1]);

525 return 0;

526 }

527 close(pfpipe[1]);

528

529 // Reading interfaces

530 int ifpipe[2];

531 pipe(ifpipe);

532 pid[1] = fork();

533 if (pid[1] == 0) {

534 if (pledge("stdio inet", ""))

535 errlog("Failed to pledge\n");

536 fetchif(ifpipe[1]);

537 return 0;

538 }

539 close(ifpipe[1]);

540

541 if (pledge("stdio wpath cpath rpath vminfo proc", ""))

542 errlog("Failed to pledge\n");

543

544 // Kernel

545 mib[0] = CTL_KERN;

546 // OS Version

547 mib[1] = KERN_OSRELEASE;

548 char version[64];

549 len = sizeof(version);

550 if (sysctl(mib, 2, version, &len, NULL, 0) == -1)

551 errlog("sysctl failed\n");

552 // OS Release

553 mib[1] = KERN_OSTYPE;

554 char osname[64];

555 len = sizeof(osname);

556 if (sysctl(mib, 2, osname, &len, NULL, 0) == -1)

557 errlog("sysctl failed\n");

558 // Hardware

559 mib[0] = CTL_HW;

560 // CPU count

561 mib[1] = HW_NCPU;

562 int ncpu;

563 len = sizeof(ncpu);

564 if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1)

565 errlog("sysctl failed\n");

566

567 // Prepare to fetch active memory

568 mib[0] = CTL_VM;

569 mib[1] = VM_UVMEXP;

570 struct uvmexp uvmexp;

571 len = sizeof(uvmexp);

572

573 struct lease leases[256];

574

575 // Fetch uptime

576 char uptime[32];

577 getuptime(uptime, sizeof(uptime));

578

579 // Fetch active memory

580 char membuf[64];

581 if (sysctl(mib, 2, &uvmexp, &len, NULL, 0) == -1)

582 errlog("sysctl failed\n");

583 snprintf(membuf, sizeof(membuf), "%!u(MISSING) MiB/%!u(MISSING) MiB",

584 uvmexp.active*uvmexp.pagesize/1024/1024, pmem/1024/1024);

585

586 // Fetch network interfaces

587 char ifbuf[4096];

588 ifbuf[0] = '\0';

589 if (read(ifpipe[0], ifbuf, 1) == 1) {

590 int count = 0;

591 if (ioctl(ifpipe[0], FIONREAD, &count) == -1) goto iferr;

592 if (count > sizeof(ifbuf)) goto iferr;

593 if (read(ifpipe[0], ifbuf+1, count) != count) goto iferr;

594 } else {

595 iferr:

596 syslog(LOG_DAEMON, "Failed to read interfaces\n");

597 ifbuf[0] = '\0';

598 }

599

600 // Fetch blocklist from pipe

601 char pfbuf[4096];

602 pfbuf[0] = '\0';

603 int r = read(pfpipe[0], pfbuf, 1);

604 if (r == 1) {

605 int count = 0;

606 if (ioctl(pfpipe[0], FIONREAD, &count) == -1) goto pferr;

607 if (count > sizeof(pfbuf)) goto pferr;

608 if (read(pfpipe[0], pfbuf+1, count) != count) goto pferr;

609 } else {

610 pferr:

611 if (r != 0)

612 syslog(LOG_DAEMON, "Failed to read pf table %!s(MISSING)\n", TABLE);

613 pfbuf[0] = '\0';

614 }

615

616 // Fetch DHCPD

617 char leasesbuf[4096];

618 leasesbuf[0] = '\0';

619 int llen = readleases(leases, 256);

620 if (llen==-1)

621 snprintf(leasesbuf, sizeof(leasesbuf), "Failed to parse leases file : %!s(MISSING)\n\n", LEASES);

622 for (int i=0; i<llen; i++)

623 snprintf(leasesbuf, sizeof(leasesbuf), dhcp_template, leasesbuf, leases[i].hostname, leases[i].ip, leases[i].date, leases[i].time, leases[i].date_end, leases[i].time_end);

624

625 printf(index_template, osname, version, cpuname, ncpu, uptime, membuf, ifbuf, leasesbuf, pfbuf);

626

627 wait(NULL);

628 close(pfpipe[0]);

629 close(ifpipe[0]);

630 return 0;

631 }

632