💾 Archived View for gemini.rmf-dev.com › repo › Vaati › RouterMonitor › files › ceb78cbbdf3af3aa556c… captured on 2023-09-28 at 16:48:59. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-09-08)

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

Go Back

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

18 // PF

19 #include <sys/socket.h>

20 #include <sys/ioctl.h>

21 #include <net/if.h>

22 #include <net/pfvar.h>

23 #include <fcntl.h>

24

25 #include "config.h"

26 #define GEMINI_PART 9

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

28

29 #include <err.h>

30 #include <syslog.h>

31

32 int fnv(const void* buf, size_t len, uint32_t hval) {

33 unsigned char *bp = (unsigned char *)buf;

34 unsigned char *be = bp + len;

35 while (bp < be) {

36 hval += (hval << 1) + (hval << 4) + (hval << 7) +

37 (hval << 8) + (hval << 24);

38 hval ^= (uint32_t)*bp++;

39 }

40 return hval;

41 }

42

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

44 {

45 printf("%i %s\r\n", code, file_mime);

46 fflush(stdout);

47 }

48

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

50 status(50, "FATAL ERROR");

51

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

53 va_list ap;

54

55 va_start(ap, format);

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

57 va_end(ap);

58

59 syslog(LOG_DAEMON, "%s", e);

60 exit(2);

61 }

62

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

64 {

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

66 va_list ap;

67

68 va_start(ap, format);

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

70 va_end(ap);

71

72 syslog(LOG_DAEMON, "%s", e);

73 exit(1);

74 }

75

76 const char* index_template =

77 "# "SYSTEM_NAME"\n\n"

78 "## System Info\n\n"

79 "* OS: %s %s\n"

80 "* CPU: %s (%d)\n"

81 "* Uptime: %s\n"

82 "* Memory: %s\n\n"

83 "## Network Info\n\n"

84 "%s"

85 #if DHCP

86 "## DHCP Leases\n\n"

87 "%s"

88 #endif

89 #if BLOCKLIST

90 "## IP Blacklist\n"

91 "%s\n"

92 #endif

93 ;

94

95 const char* if_template =

96 "%s"

97 "### %s\n"

98 "IP: %s\n"

99 "Bytes: %.2f GB\n"

100 "Packets: %lld\n\n";

101

102 #if DHCP

103 const char* dhcp_template =

104 "%s"

105 "### %s\n"

106 "IP: %s\n"

107 "Start: %s %s\n"

108 "End: %s %s\n\n";

109 #endif

110

111 #if BLOCKLIST

112 const char* block_template =

113 "%s"

114 "* %s\n";

115 #endif

116

117

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

119

120 int dev_pf = -1;

121

122 void pfinit() {

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

124 if (dev_pf < 0)

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

126 struct pf_status status;

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

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

129 if (!status.running)

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

131 }

132

133 struct pfr_addr* getpfaddrs(char* table, struct pfr_addr* addr, ssize_t* len) {

134 if (dev_pf == -1)

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

136

137 struct pfioc_table io;

138 struct pfr_table tbl;

139

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

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

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

143 io.pfrio_table = tbl;

144 io.pfrio_buffer = addr;

145 io.pfrio_esize = sizeof(*addr);

146 io.pfrio_size = *len;

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

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

149 if (io.pfrio_size > *len)

150 return NULL;

151 *len = io.pfrio_size;

152 return addr;

153 }

154

155 struct if_info {

156 unsigned long long bytes;

157 unsigned long long packets;

158 char name[64];

159 char host[NI_MAXHOST];

160 };

161

162 struct if_info ifinfo[MAX_INTERFACES];

163 struct if_info *getifinfo(char* name) {

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

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

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

167 return &ifinfo[i];

168 }

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

170 return &ifinfo[i];

171 }

172 return NULL;

173 }

174

175 void fetchif(int fd) {

176 explicit_bzero(ifinfo, sizeof(ifinfo));

177 struct ifaddrs *ifap;

178 if (getifaddrs(&ifap)) {

179 syslog(LOG_DAEMON, "Failed to fetch network interfaces(%d)\n",

180 errno);

181 return;

182 }

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

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

185 ifp->ifa_name[1] == 'o')

186 continue;

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

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

189 struct if_data *stats = ifp->ifa_data;

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

191 continue;

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

193 if (!info) {

194 syslog(LOG_DAEMON,

195 "Too many network interface (above %d)\n",

196 MAX_INTERFACES);

197 break;

198 }

199 info->bytes = stats->ifi_ibytes;

200 info->packets = stats->ifi_ipackets;

201 continue;

202 }

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

204 continue;

205 char hbuf[NI_MAXHOST];

206 if (getnameinfo(ifp->ifa_addr,

207 sizeof(*(ifp->ifa_addr)), hbuf, sizeof(hbuf),

208 NULL, 0, NI_NUMERICHOST) == 0) {

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

210 if (!info) {

211 syslog(LOG_DAEMON,

212 "Too many network interface (above %d)\n",

213 MAX_INTERFACES);

214 break;

215 }

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

217 }

218 }

219 freeifaddrs(ifap);

220 char ifbuf[4096];

221 ifbuf[0] = '\0';

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

223 snprintf(ifbuf, sizeof(ifbuf), if_template, ifbuf,

224 ifinfo[i].name, ifinfo[i].host,

225 (float)ifinfo[i].bytes/1000000000,

226 ifinfo[i].packets);

227 }

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

229 }

230

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

232 struct timespec boottime;

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

234 return -1;

235 time_t uptime = boottime.tv_sec;

236 if (uptime > 59) {

237 uptime += 30;

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

239 uptime %= (3600*24);

240 int hrs = uptime / 3600;

241 uptime %= 3600;

242 int mins = uptime / 60;

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

244 if (days > 0)

245 snprintf(out, len, "%d day%s, ",

246 days, days > 1 ? "s" : "");

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

248 snprintf(out, len, "%s%d hour%s, %02d min%s",

249 out, hrs, hrs > 1 ? "s" : "",

250 mins, mins > 1 ? "s" : "");

251 else {

252 if (hrs > 0)

253 snprintf(out, len, "%s%d hour%s",

254 out, hrs, hrs > 1 ? "s" : "");

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

256 snprintf(out, len, "%s%d min%s",

257 out, mins,

258 mins > 1 ? "s" : "");

259 }

260 } else {

261 snprintf(out, len, "%lld", uptime);

262 }

263 return 0;

264 }

265

266 #if DHCP

267 struct lease {

268 char ip[32];

269 char hostname[256];

270 char date[32];

271 char time[32];

272 char date_end[32];

273 char time_end[32];

274 };

275

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

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

278 if (!f)

279 return -1;

280 char* line = NULL;

281 size_t length = 0;

282 ssize_t read;

283

284 int i = 0;

285 int j = 0;

286 int hostname_found = 0;

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

288 if (i >= len) break;

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

290 strstr(line, "lease") < strchr(line, '{')) {

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

292 if (!ip)

293 goto failed;

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

295 if (!ptr)

296 goto failed;

297 ptr[0] = '\0';

298 int exist = 0;

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

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

301 exist = 1;

302 break;

303 }

304 }

305 if (!exist) {

306 strlcpy(leases[i].ip, ip,

307 sizeof(leases[i].ip));

308 j = 1;

309 }

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

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

312 if (!hostname)

313 goto failed;

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

315 strlcpy(leases[i].hostname, hostname,

316 sizeof(leases[i].hostname));

317 j++;

318 hostname_found = 1;

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

320 int start = 1;

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

322 if (!ptr) {

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

324 start = 0;

325 }

326 int word = 0;

327 int inspace = 0;

328 char* date = NULL;

329 char* time = NULL;

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

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

332 inspace = 1;

333 else if (inspace) {

334 inspace = 0;

335 word++;

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

337 if (word==3) {

338 time = ptr;

339 break;

340 }

341 }

342 }

343 if (word!=3) goto failed;

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

345 if (!space) goto failed;

346 *space = 0;

347 space = strchr(time, ' ');

348 if (!space) goto failed;

349 *space = 0;

350 strlcpy(start?leases[i].date:leases[i].date_end,

351 date, sizeof(leases[i].date));

352 strlcpy(start?leases[i].time:leases[i].time_end,

353 time, sizeof(leases[i].time));

354 j++;

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

356 if (!hostname_found) {

357 strlcpy(leases[i].hostname, "Unknown",

358 sizeof(leases[i].hostname));

359 j++;

360 }

361 hostname_found = 0;

362 if (j>3) {

363 time_t t = time(NULL);

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

365 int date[3];

366 int time[3];

367 int pos, last;

368 last = pos = 0;

369 char buf[32];

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

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

372 int end = 0;

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

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

375 buf[i] = '\0';

376 date[pos] = strtol(&buf[last],

377 NULL, 10);

378 if (end) break;

379 pos++;

380 last = i+1;

381 }

382 }

383 date[0] -= 1900;

384 date[1] -= 1;

385 time[1] -= 1;

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

387 pos = last = 0;

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

389 int end = 0;

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

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

392 buf[i] = '\0';

393 time[pos] = strtol(&buf[last],

394 NULL, 10);

395 if (end) break;

396 pos++;

397 last = i+1;

398 }

399 }

400 j = 0;

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

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

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

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

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

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

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

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

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

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

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

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

413 }

414 j = 0;

415 }

416 }

417 free(line);

418 return i;

419 failed:

420 free(line);

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

422 return -1;

423 }

424 #endif

425

426 #if BLOCKLIST

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

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

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

430 }

431

432 void fetchaddrs(int fd) {

433 struct pfr_addr addrs[ADDRS];

434 ssize_t length = ADDRS;

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

436 char list[4096];

437 list[0] = '\0';

438

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

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

441 char ip[32];

442 ipv4str(addrs_ptr[i].pfra_u._pfra_ip4addr.s_addr,

443 ip, sizeof(ip));

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

445 }

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

447 }

448 #endif

449

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

451

452 if (argc > 1) {

453 printf("0x%x\n", fnv(argv[1], strlen(argv[1]), 1234));

454 return 0;

455 }

456

457 struct passwd *p;

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

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

460

461 // Open pf, require root

462 pfinit();

463

464 // Only needs to read leases file

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

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

467

468 // Drop privileges

469 if (setgroups(0, NULL))

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

471 if (setgid(p->pw_gid))

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

473 if (setuid(p->pw_uid))

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

475

476 #if REQUEST

477 // Read request from stdin

478 char request[1024];

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

480 if (feof(stdin)) {

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

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

483 } else if (ferror(stdin)) {

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

485 errlogn("error while reading request: %s", request);

486 }

487 }

488

489 // Check if request is complete

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

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

492 errlogn("request is too long (1024 max): %s", request);

493 }

494

495 // Remove \r character

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

497 if (pos != NULL)

498 *pos = '\0';

499

500 // Check if gemini:// is there

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

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

503

504 // Hide password in the log

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

506 if (pos)

507 *pos = '\0';

508 syslog(LOG_DAEMON, "request: %s\n", request);

509 if (pos)

510 *pos = '?';

511

512 char* hostname = request+GEMINI_PART;

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

514 if (pos != NULL) {

515 pos[0] = '\0';

516 pos++;

517 }

518

519 // Check if hostname is the right one

520 if (strcmp(hostname, HOSTNAME)) {

521 status(59, "invalid hostname");

522 errlogn("wrong hostname expecting %s, got %s",

523 HOSTNAME, hostname);

524 }

525

526 #if PASSWORD

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

528 // Ask for password

529 status(11, "Password");

530 return 0;

531 } else {

532 // Check password

533 pos++;

534 uint32_t password = PASSWORD;

535 uint32_t hash = fnv((unsigned char*)pos, strlen(pos), 1234);

536

537 if (hash != password) {

538 status(59, "invalid password");

539 return 0;

540 }

541 }

542 #endif

543

544 // Write header

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

546 #endif

547

548 // Hardware info

549 int mib[2];

550 size_t len;

551 mib[0] = CTL_HW;

552 // CPU Name

553 mib[1] = HW_MODEL;

554 char cpuname[64];

555 len = sizeof(cpuname);

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

557 errlog("sysctl failed\n");

558 // Physical memory

559 mib[1] = HW_PHYSMEM64;

560 size_t pmem;

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

562 len = sizeof(pmem);

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

564 errlog("sysctl failed\n");

565

566 #if BLOCKLIST

567 // Reading addresses

568 int pfpipe[2];

569 pipe(pfpipe);

570 pid[0] = fork();

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

572 // No pledge allows to do that

573 fetchaddrs(pfpipe[1]);

574 return 0;

575 }

576 close(pfpipe[1]);

577 #endif

578

579 // Reading interfaces

580 int ifpipe[2];

581 pipe(ifpipe);

582 pid[1] = fork();

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

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

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

586 fetchif(ifpipe[1]);

587 return 0;

588 }

589 close(ifpipe[1]);

590

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

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

593

594 // Kernel

595 mib[0] = CTL_KERN;

596 // OS Version

597 mib[1] = KERN_OSRELEASE;

598 char version[64];

599 len = sizeof(version);

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

601 errlog("sysctl failed\n");

602 // OS Release

603 mib[1] = KERN_OSTYPE;

604 char osname[64];

605 len = sizeof(osname);

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

607 errlog("sysctl failed\n");

608 // Hardware

609 mib[0] = CTL_HW;

610 // CPU count

611 mib[1] = HW_NCPU;

612 int ncpu;

613 len = sizeof(ncpu);

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

615 errlog("sysctl failed\n");

616

617 // Prepare to fetch active memory

618 mib[0] = CTL_VM;

619 mib[1] = VM_UVMEXP;

620 struct uvmexp uvmexp;

621 len = sizeof(uvmexp);

622

623 // Fetch uptime

624 char uptime[32];

625 getuptime(uptime, sizeof(uptime));

626

627 // Fetch active memory

628 char membuf[64];

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

630 errlog("sysctl failed\n");

631 snprintf(membuf, sizeof(membuf), "%u MiB/%lu MiB",

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

633

634 // Fetch network interfaces

635 char ifbuf[4096] = {0};

636 if (read(ifbuf[0], ifbuf, sizeof(ifbuf)) < 1) {

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

638 ifbuf[0] = '\0';

639 }

640

641 #if BLOCKLIST

642 // Fetch blocklist from pipe

643 char pfbuf[4096] = {0};

644 if (read(pfpipe[0], pfbuf, sizeof(pfbuf)) < 1) {

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

646 pfbuf[0] = '\0';

647 }

648 #endif

649

650 #if DHCP

651 // Fetch DHCPD

652 struct lease leases[256];

653 char leasesbuf[4096];

654 leasesbuf[0] = '\0';

655 int llen = readleases(leases, 256);

656 if (llen==-1)

657 snprintf(leasesbuf, sizeof(leasesbuf),

658 "Failed to parse leases file : %s\n\n", LEASES);

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

660 snprintf(leasesbuf, sizeof(leasesbuf), dhcp_template,

661 leasesbuf, leases[i].hostname, leases[i].ip,

662 leases[i].date, leases[i].time,

663 leases[i].date_end, leases[i].time_end);

664 #endif

665

666 printf(index_template, osname, version, cpuname, ncpu, uptime, membuf,

667 ifbuf

668 #if DHCP

669 ,leasesbuf

670 #endif

671 #if BLOCKLIST

672 ,pfbuf

673 #endif

674 );

675

676 wait(NULL);

677 #if BLOCKLIST

678 close(pfpipe[0]);

679 #endif

680 close(ifpipe[0]);

681 return 0;

682 }

683