📄 radrelay.c
字号:
/* * radrelay.c This program tails a detail logfile, reads the log * entries, forwards them to a remote radius server, * and moves the processed records to another file. * * Used to replicate accounting records to one (central) * server - works even if remote server has extended * downtime, and/or if this program is restarted. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2001 Cistron Internet Services B.V. * Copyright 2002 Simon Ekstrand <simon@routemeister.net> * */char radrelay_rcsid[] ="$Id: radrelay.c,v 1.22.2.2 2005/05/23 09:35:09 nbk Exp $";#include "autoconf.h"#include "libradius.h"#include <sys/types.h>#include <sys/socket.h>#include <sys/time.h>#include <sys/stat.h>#ifdef HAVE_NETINET_IN_H#include <netinet/in.h>#endif#include <stdio.h>#include <fcntl.h>#include <time.h>#include <unistd.h>#include <netdb.h>#include <stdlib.h>#include <signal.h>#include <errno.h>#include <string.h>#include "radiusd.h"#include "conf.h"#include "radpaths.h"#include "missing.h"#include "conffile.h"const char *progname;int debug_flag = 0;const char *radlog_dir = NULL;radlog_dest_t radlog_dest = RADLOG_FILES;const char *radius_dir = NULL;const char *radacct_dir = NULL;const char *radlib_dir = NULL;uint32_t myip = INADDR_ANY;int log_stripped_names;struct main_config_t mainconfig;/* * Possible states for request->state */#define STATE_EMPTY 0#define STATE_BUSY1 1#define STATE_BUSY2 2#define STATE_FULL 3/* * Possible states for the loop() function. */#define STATE_RUN 0#define STATE_BACKLOG 1#define STATE_WAIT 2#define STATE_SHUTDOWN 3#define STATE_CLOSE 4#define NR_SLOTS 64/* * A relay request. */struct relay_request { int state; /* REQ_* state */ time_t retrans; /* when to retrans */ unsigned int retrans_num; /* Number of retransmissions */ time_t timestamp; /* orig recv time */ uint32_t client_ip; /* Client-IP-Addr */ RADIUS_PACKET *req; /* Radius request */};struct relay_misc { int sockfd; /* Main socket descriptor */ uint32_t dst_addr; /* Destination address */ short dst_port; /* Destination port */ uint32_t src_addr; /* Source address */ char detail[1024]; /* Detail file */ char *secret; /* Secret */ char f_secret[256]; /* File secret */};/* * Used for reading the client configurations from the config files. */char *c_secret = NULL;char *c_shortname = NULL;struct relay_request slots[NR_SLOTS];char id_map[256];int request_head = 0;int got_sigterm = 0;int debug = 0;int get_radius_id(void);void sigterm_handler(int sig);void ms_sleep(int msec);int isdateline(char *d);int read_one(FILE *fp, struct relay_request *req);int do_recv(struct relay_misc *r_args);int do_send(struct relay_request *r, char *secret);int detail_move(char *from, char *to);void loop(struct relay_misc *r_args);int find_shortname(char *shortname, char **host, char **secret);void usage(void);/* * Get a radius id which is not * currently being used (outstanding request) * Since NR_SLOTS < 256 we can't * have more outstanding requests than radius ids */int get_radius_id(){ unsigned int id = 0; for(id = 0; id < 256; id++){ if (id_map[id] == 0) break; } if (id == 256 || id_map[id] != 0){ fprintf(stdout, "get_radius_id(): No IDs available. Something is very wrong\n"); return -1; } id_map[id] = 1; fprintf(stdout, "get_radius_id(): Assign RADIUS ID = %d\n",id); return id;}void sigterm_handler(int sig){ signal(sig, sigterm_handler); got_sigterm = 1;}/* * Sleep a number of milli seconds */void ms_sleep(int msec){ struct timeval tv; tv.tv_sec = (msec / 1000); tv.tv_usec = (msec % 1000) * 1000; select(0, NULL, NULL, NULL, &tv);}/* * Does this (remotely) look like "Tue Jan 23 06:55:48 2001" ? */int isdateline(char *d){ int y; return sscanf(d, "%*s %*s %*d %*d:%*d:%*d %d", &y);}/* * Read one request from the detail file. * Note that the file is locked during the read, and that * we return *with the file locked* if we reach end-of-file. * * STATE_EMPTY: Slot is empty. * STATE_BUSY1: Looking for start of a detail record (timestamp) * STATE_BUSY2: Reading the A/V pairs of a detail record. * STATE_FULL: Read the complete record. * */int read_one(FILE *fp, struct relay_request *r_req){ VALUE_PAIR *vp; char *s; char buf[256]; char key[32], val[32]; int skip; long fpos; int x; unsigned int i = 0; /* Never happens */ if (r_req->state == STATE_FULL) return 0; if (r_req->state == STATE_EMPTY) { r_req->state = STATE_BUSY1; } /* * Try to lock the detail-file. * If lockf is used we want to lock the _whole_ file, hence the * fseek to the start of the file. */ fpos = ftell(fp); fseek(fp, 0L, SEEK_SET); do { x = rad_lockfd_nonblock(fileno(fp), 0); if (x == -1) ms_sleep(100); } while (x == -1 && i++ < 20); if (x == -1) return 0;redo: s = NULL; fseek(fp, fpos, SEEK_SET); fpos = ftell(fp); while ((s = fgets(buf, sizeof(buf), fp)) != NULL) { /* * Eek! We've just read a broken attribute. * This does seem to happen every once in a long while * due to some quirk involving threading, multiple processes * going for the detail file lock at once and writes not * being flushed properly. Things should be ok next time * around. */ if (!strlen(buf)) { fprintf(stdout, "read_one: ZERO BYTE\n"); fseek(fp, fpos + 1, SEEK_SET); break; } else if (buf[strlen(buf) - 1] != '\n') { fprintf(stdout, "read_one: BROKEN ATTRIBUTE\n"); fseek(fp, fpos + strlen(buf), SEEK_SET); break; } if (r_req->state == STATE_BUSY1) { if (isdateline(buf)) { r_req->state = STATE_BUSY2; } } else if (r_req->state == STATE_BUSY2) { if (buf[0] != ' ' && buf[0] != '\t') { r_req->state = STATE_FULL; break; } /* * Found A/V pair, but we skip non-protocol * values. */ skip = 0; if (sscanf(buf, "%31s = %31s", key, val) == 2) { if (!strcasecmp(key, "Timestamp")) { r_req->timestamp = atoi(val); skip++; } else if (!strcasecmp(key, "Client-IP-Address")) { r_req->client_ip = ip_getaddr(val); skip++; } else if (!strcasecmp(key, "Request-Authenticator")) skip++; } if (!skip) { vp = NULL; if (userparse(buf, &vp) > 0 && (vp != NULL) && (vp->attribute < 256 || vp->attribute > 65535) && vp->attribute != PW_VENDOR_SPECIFIC) { pairadd(&(r_req->req->vps), vp); } else { pairfree(&vp); } } } fpos = ftell(fp); } clearerr(fp); if (r_req->state == STATE_FULL) { /* * w00 - we just completed reading a record in full. */ /* * Check that we have an Acct-Status-Type attribute. If not * reject the record */ if (pairfind(r_req->req->vps, PW_ACCT_STATUS_TYPE) == NULL){ fprintf(stdout, "read_one: No Acct-Status-Type attribute present. Rejecting record.\n"); r_req->state = STATE_BUSY1; if (r_req->req->vps != NULL) { pairfree(&r_req->req->vps); r_req->req->vps = NULL; } if (r_req->req->data != NULL) { free (r_req->req->data); r_req->req->data = NULL; } r_req->retrans = 0; r_req->retrans_num = 0; r_req->timestamp = 0; r_req->client_ip = 0; goto redo; } if (r_req->timestamp == 0) r_req->timestamp = time(NULL); if ((vp = pairfind(r_req->req->vps, PW_ACCT_DELAY_TIME)) != NULL) { r_req->timestamp -= vp->lvalue; vp->lvalue = 0; } r_req->req->id = get_radius_id(); } if (s == NULL) { /* * Apparently we reached end of file. If we didn't * partially read a record, we let the caller know * we're at end of file. */ if (r_req->state == STATE_BUSY1) { r_req->state = STATE_EMPTY; } if (r_req->state == STATE_EMPTY || r_req->state == STATE_FULL) return EOF; } fpos = ftell(fp); fseek(fp, 0L, SEEK_SET); rad_unlockfd(fileno(fp), 0); fseek(fp, fpos, SEEK_SET); return 0;}/* * Receive answers from the remote server. */int do_recv(struct relay_misc *r_args){ RADIUS_PACKET *rep; struct relay_request *r; int i; /* * Receive packet and validate it's length. */ rep = rad_recv(r_args->sockfd); if (rep == NULL) { librad_perror("radrelay:"); return -1; } /* * Must be an accounting response. * FIXME: check if this is the right server! */ if (rep->code != PW_ACCOUNTING_RESPONSE) return -1; /* * Decode packet into radius attributes. */ /* * Now find it in the outstanding requests. */ for (i = 0; i < NR_SLOTS; i++) { r = slots + i; if (r->state == STATE_FULL && r->req->id == rep->id) { if (rad_decode(rep, r->req, r_args->secret) != 0) { librad_perror("rad_decode"); return -1; } /* * Got it. Clear slot. * FIXME: check reponse digest ? */ id_map[r->req->id] = 0; fprintf(stdout, "do_recv: Free RADIUS ID = %d\n",r->req->id); if (r->req->vps != NULL) { pairfree(&r->req->vps); r->req->vps = NULL; } if (r->req->data != NULL) { free (r->req->data); r->req->data = NULL; } r->state = STATE_EMPTY; r->retrans = 0; r->retrans_num = 0; r->timestamp = 0; r->client_ip = 0; break; } } rad_free(&rep); return 0;}/* * Send accounting packet to remote server. */int do_send(struct relay_request *r, char *secret){ VALUE_PAIR *vp; time_t now; /* * Prevent loops. */ if (r->client_ip == r->req->dst_ipaddr) { fprintf(stdout, "do_send: Client-IP == Dest-IP. Droping packet.\n"); fprintf(stdout, "do_send: Free RADIUS ID = %d\n",r->req->id); id_map[r->req->id] = 0; if (r->req->vps != NULL) { pairfree(&r->req->vps); r->req->vps = NULL; } if (r->req->data != NULL) { free (r->req->data); r->req->data = NULL; } r->state = STATE_EMPTY; r->retrans = 0; r->retrans_num = 0; r->timestamp = 0; r->client_ip = 0; return 0; } /* * Has the time come for this packet ? */ now = time(NULL); if (r->retrans > now) return 0; /* * If we are resending a packet we *need* to * change the radius packet id since the request * authenticator is different (due to different * Acct-Delay-Time value). * Otherwise the radius server may consider the * packet a duplicate and we 'll get caught in a * loop. */ if (r->retrans > 0){ id_map[r->req->id] = 0; r->req->id = get_radius_id(); if (r->req->data != NULL){ free(r->req->data); r->req->data = NULL; } r->retrans_num++; } if (r->retrans_num > 20) r->retrans = now + 70; else r->retrans = now + 3 + (3 * r->retrans_num); /* * Find the Acct-Delay-Time attribute. If it's * not there, add one. */ if ((vp = pairfind(r->req->vps, PW_ACCT_DELAY_TIME)) == NULL) { vp = paircreate(PW_ACCT_DELAY_TIME, PW_TYPE_INTEGER); pairadd(&(r->req->vps), vp); } vp->lvalue = (now - r->timestamp); /* * Rebuild the entire packet every time from * scratch - the signature changed because * Acct-Delay-Time changed. */ rad_send(r->req, NULL, secret); return 1;}/* * Rename a file, then recreate the old file with the * same permissions and zero size. */int detail_move(char *from, char *to){ struct stat st; int n; int oldmask; if (stat(from, &st) < 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -