1 | /*************************************** 2 | 3 | Protocol mirror module (pw). 4 | 5 | Status: NOT REVUED, NOT TESTED 6 | 7 | ******************/ /****************** 8 | Filename : protocol_mirror.c 9 | Author : andrei 10 | OSs Tested : Solaris 11 | ******************/ /****************** 12 | Copyright (c) 2000 RIPE NCC 13 | 14 | All Rights Reserved 15 | 16 | Permission to use, copy, modify, and distribute this software and its 17 | documentation for any purpose and without fee is hereby granted, 18 | provided that the above copyright notice appear in all copies and that 19 | both that copyright notice and this permission notice appear in 20 | supporting documentation, and that the name of the author not be 21 | used in advertising or publicity pertaining to distribution of the 22 | software without specific, written prior permission. 23 | 24 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 25 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL 26 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 27 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 28 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 29 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 30 | ***************************************/ 31 | #include <stdio.h> 32 | #include <glib.h> 33 | 34 | #include "protocol_mirror.h" 35 | #include "mysql_driver.h" 36 | #include "constants.h" 37 | 38 | //#include "access_control.h" 39 | #include "sk.h" 40 | #include "stubs.h" 41 | #include "ud.h" 42 | #include "ta.h" 43 | 44 | #include "ca_configFns.h" 45 | #include "ca_dictionary.h" 46 | #include "ca_macros.h" 47 | #include "ca_srcAttribs.h" 48 | 49 | #include "erroutines.h" 50 | 51 | #include "getopt.h" 52 | 53 | #define MIN_ARG_LENGTH 6 54 | #define NRTM_DELIM "-:" 55 | 56 | #define MAX_OPT_ARG_C 3 57 | 58 | #define Q_QUERY 0x00 59 | #define G_QUERY 0x01 60 | #define K_QUERY 0x02 61 | 62 | #define IS_Q_QUERY(a) ((a)&Q_QUERY) 63 | #define IS_G_QUERY(a) ((a)&G_QUERY) 64 | #define IS_PERSISTENT(a) ((a)&K_QUERY) 65 | 66 | 67 | /* 68 | * parses input and fills nrtm_q_t structure 69 | * 70 | * Returns: 71 | * -1 in case of garbage 72 | * 0 in case of -q sources 73 | * 1 in case of valid -g 74 | * 2 in case of -k 75 | */ 76 | static int parse_request(char *input, nrtm_q_t *nrtm_q) 77 | { 78 | int res=0, err=0; 79 | int opt_argc; 80 | int c; 81 | gchar **opt_argv; 82 | getopt_state_t *gst = NULL; 83 | 84 | /* Create the arguments. */ 85 | /* This allows only a maximum of MAX_OPT_ARG_C words in the query. */ 86 | opt_argv = g_strsplit(input, " ", MAX_OPT_ARG_C); 87 | 88 | /* Determine the number of arguments. */ 89 | for (opt_argc=0; opt_argv[opt_argc] != NULL; opt_argc++); 90 | 91 | dieif( (gst = mg_new(0)) == NULL ); 92 | 93 | while ((c = mg_getopt(opt_argc, opt_argv, "kq:g:", gst)) != EOF) 94 | { 95 | switch (c) { 96 | case 'k': 97 | res |= K_QUERY; /* persistent connection */ 98 | break; 99 | 100 | case 'q': 101 | if (gst->optarg != NULL) { 102 | char *token, *cursor = gst->optarg; 103 | 104 | res |= Q_QUERY; 105 | err=strncmp(cursor, "sources", 7); 106 | if(err!=0) break; 107 | cursor+=7; 108 | g_strchug(cursor); 109 | token=cursor; 110 | /* if no sourses are specified - put NULL in nrtm_q->source and list them all */ 111 | if ((*token=='\0') || (*token=='\n') || ((int)*token==13))nrtm_q->source=NULL; 112 | else { 113 | cursor=index(token, ' '); 114 | if (cursor) nrtm_q->source=g_strndup(token, (cursor-token)); 115 | else { 116 | cursor=index(token, 13); /* search for ctrl-M - telnet loves this */ 117 | if (cursor) nrtm_q->source=g_strndup(token, (cursor-token)); 118 | else { 119 | cursor=index(token, '\n'); 120 | if (cursor) nrtm_q->source=g_strndup(token, (cursor-token)); 121 | else nrtm_q->source=g_strdup(token); 122 | } 123 | } 124 | } 125 | } else err=1; 126 | break; 127 | 128 | case 'g': 129 | if (gst->optarg != NULL) { 130 | char *token, *cursor = gst->optarg; 131 | char **tokens; 132 | 133 | res |= G_QUERY; 134 | g_strdelimit(cursor, NRTM_DELIM, ':'); 135 | tokens=g_strsplit(cursor, ":", 4); 136 | if(tokens==NULL) { err=1; break; } 137 | 138 | if(tokens[0]) { 139 | /* first token is source name */ 140 | nrtm_q->source=g_strdup(tokens[0]); 141 | if(tokens[1]) { 142 | /* second token is version number */ 143 | nrtm_q->version=atoi(tokens[1]); 144 | if(tokens[2]) { 145 | /* this is first serial */ 146 | nrtm_q->first=atol(tokens[2]); 147 | if (nrtm_q->first>0) { 148 | if(tokens[3]) { 149 | /* this is last serial */ 150 | nrtm_q->last=atol(tokens[3]); 151 | if (nrtm_q->last==0) 152 | if (strncasecmp(tokens[3], "LAST", 4)!=0) err=1; 153 | } else err=1; 154 | } else err=1; 155 | } else err=1; 156 | } else err=1; 157 | } else err=1; 158 | g_strfreev(tokens); 159 | 160 | } else err=1; 161 | 162 | break; 163 | default: 164 | err=1; 165 | break; 166 | } /* switch */ 167 | } /* while there are arguments */ 168 | 169 | free(gst); 170 | 171 | if (err) return(-1); 172 | else return(res); 173 | 174 | } 175 | 176 | 177 | /* PM_interact() */ 178 | /*++++++++++++++++++++++++++++++++++++++ 179 | Interact with the client. 180 | 181 | int sock Socket that client is connected to. 182 | 183 | More: 184 | +html+ <PRE> 185 | Authors: 186 | ottrey 187 | andrei 188 | 189 | +html+ </PRE><DL COMPACT> 190 | +html+ <DT>Online References: 191 | +html+ <DD><UL> 192 | +html+ </UL></DL> 193 | 194 | ++++++++++++++++++++++++++++++++++++++*/ 195 | void PM_interact(int sock) { 196 | char input[MAX_INPUT_SIZE]; 197 | char buff[STR_L]; 198 | ca_dbSource_t *source_hdl; 199 | int read_result; 200 | int parse_result; 201 | ip_addr_t address; 202 | 203 | char *hostaddress=NULL; 204 | sk_conn_st condat; 205 | nrtm_q_t nrtm_q; 206 | long current_serial; 207 | long oldest_serial; 208 | 209 | char *object; 210 | int operation; 211 | 212 | 213 | char *db_host; 214 | int db_port; 215 | char *db_name; 216 | char *db_user; 217 | char *db_pswd; 218 | 219 | GString *gbuff; 220 | 221 | SQ_connection_t *sql_connection; 222 | int persistent_connection; 223 | 224 | /* make a record for thread accounting */ 225 | TA_add(sock, "nrtm_srv"); 226 | 227 | 228 | /* Get the IP of the client */ 229 | hostaddress = SK_getpeername(sock); 230 | 231 | /* initialise the connection structure */ 232 | memset( &condat, 0, sizeof(sk_conn_st)); 233 | /* initialise the nrtm structure */ 234 | memset( &nrtm_q, 0, sizeof(nrtm_q_t)); 235 | /* set the connection data: both rIP and eIP to real IP */ 236 | condat.sock = sock; 237 | condat.ip = hostaddress; 238 | SK_getpeerip(sock, &(condat.rIP)); 239 | memcpy( &(condat.eIP), &(condat.rIP), sizeof(ip_addr_t)); 240 | 241 | 242 | /* Read input */ 243 | read_result = SK_cd_gets(&(condat), input, MAX_INPUT_SIZE); 244 | 245 | /* read_result < 0 is an error and connection should be closed */ 246 | if (read_result < 0 ) { 247 | /* log the fact, rtc was set */ 248 | } 249 | 250 | 251 | parse_result = parse_request(input, &nrtm_q); 252 | 253 | 254 | if (parse_result < 0 ) { 255 | ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Garbage received: %s", hostaddress, input); 256 | /* log the fact and exit */ 257 | /* Free the hostaddress */ 258 | sprintf(buff, "\n%%ERROR:1: Syntax error\n\n"); 259 | SK_cd_puts(&condat, buff); 260 | SK_cd_close(&(condat)); 261 | free(hostaddress); 262 | free(nrtm_q.source); 263 | return; 264 | } 265 | 266 | ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] -- input: [%s]", hostaddress, input); 267 | 268 | /* this is -q sources query - answer and return */ 269 | if (IS_Q_QUERY(parse_result)) { 270 | 271 | gbuff=PM_get_nrtm_sources(&(condat.rIP), nrtm_q.source); 272 | SK_cd_puts(&condat, gbuff->str); 273 | /* Free allocated memory */ 274 | g_string_free(gbuff, TRUE); 275 | free(hostaddress); 276 | free(nrtm_q.source); 277 | SK_cd_close(&(condat)); 278 | return; 279 | } 280 | else if(IS_G_QUERY(parse_result)){ 281 | if(IS_PERSISTENT(parse_result))persistent_connection=1; else persistent_connection=0; 282 | } 283 | else { 284 | ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Syntax error: %s", hostaddress, input); 285 | /* log the fact and exit */ 286 | /* Free the hostaddress */ 287 | sprintf(buff, "\n%%ERROR:1: Syntax error\n\n"); 288 | SK_cd_puts(&condat, buff); 289 | SK_cd_close(&(condat)); 290 | free(hostaddress); 291 | free(nrtm_q.source); 292 | return; 293 | 294 | } 295 | 296 | /* otherwise this is -g query */ 297 | 298 | 299 | ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] -- input parsed: %s:%d:%ld-%ld", hostaddress, nrtm_q.source, nrtm_q.version, nrtm_q.first, nrtm_q.last); 300 | 301 | source_hdl = ca_get_SourceHandleByName(nrtm_q.source); 302 | if (source_hdl == NULL){ 303 | ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Unknown source %s", hostaddress, nrtm_q.source); 304 | sprintf(buff, "\n%%ERROR:4: Unknown source\n\n"); 305 | SK_cd_puts(&condat, buff); 306 | free(hostaddress); 307 | free(nrtm_q.source); 308 | SK_cd_close(&(condat)); 309 | return; 310 | } 311 | 312 | /* check if the client is authorized to mirror */ 313 | SK_getpeerip(sock, &address); 314 | if(!AA_can_mirror(&address, nrtm_q.source)){ 315 | ER_inf_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Not authorized to mirror the source %s", hostaddress, nrtm_q.source); 316 | sprintf(buff, "\n%%ERROR:3: You are not authorized to mirror the database\n\n"); 317 | SK_cd_puts(&condat, buff); 318 | free(hostaddress); 319 | free(nrtm_q.source); 320 | SK_cd_close(&(condat)); 321 | return; 322 | } 323 | 324 | 325 | 326 | /* get database */ 327 | db_name = ca_get_srcdbname(source_hdl); 328 | /* get database host*/ 329 | db_host = ca_get_srcdbmachine(source_hdl); 330 | /* get database port*/ 331 | db_port = ca_get_srcdbport(source_hdl); 332 | /* get database user*/ 333 | db_user = ca_get_srcdbuser(source_hdl); 334 | /* get database password*/ 335 | db_pswd = ca_get_srcdbpassword(source_hdl); 336 | 337 | sql_connection = SQ_get_connection(db_host, db_port,db_name, db_user, db_pswd); 338 | if(!sql_connection) { 339 | ER_perror(FAC_PM, PM_NOSQLC," database='%s' [%d] %s",db_name, SQ_errno(sql_connection), SQ_error(sql_connection)); 340 | return; 341 | } 342 | ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] -- Made SQL connection to %s@%s", hostaddress, db_name, db_host); 343 | 344 | /* free copies of the variables */ 345 | free(db_host); 346 | free(db_name); 347 | free(db_user); 348 | free(db_pswd); 349 | 350 | current_serial=PM_get_current_serial(sql_connection); 351 | oldest_serial=PM_get_oldest_serial(sql_connection); 352 | 353 | if((current_serial==-1) || (oldest_serial==-1)) { 354 | ER_perror(FAC_PM, PM_NOSERN," database='%s' [%d] %s",db_name, SQ_errno(sql_connection), SQ_error(sql_connection)); 355 | /* Free the hostaddress */ 356 | SK_cd_close(&(condat)); 357 | /* close the connection to SQL server */ 358 | SQ_close_connection(sql_connection); 359 | free(hostaddress); 360 | free(nrtm_q.source); 361 | return; 362 | } 363 | 364 | /* zero indicates that LAST keyword has been used */ 365 | if(nrtm_q.last==0)nrtm_q.last=current_serial; 366 | /* for persistent connections end of range has no meaning */ 367 | if(persistent_connection)nrtm_q.last=current_serial; 368 | 369 | 370 | if((nrtm_q.first>nrtm_q.last) || (nrtm_q.first<oldest_serial) || (nrtm_q.last>current_serial) || 371 | (nrtm_q.first<=0) || (nrtm_q.last<=0) ) 372 | { 373 | ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Invalid range: %ld-%ld", hostaddress, nrtm_q.first, nrtm_q.last); 374 | /* write error message back to the client */ 375 | sprintf(buff, "\n%%ERROR:2: Invalid range: Not within %ld-%ld\n\n", oldest_serial, current_serial); 376 | SK_cd_puts(&condat, buff); 377 | SK_cd_close(&(condat)); 378 | 379 | /* close the connection to SQL server */ 380 | SQ_close_connection(sql_connection); 381 | 382 | /* Free the hostaddress */ 383 | free(hostaddress); 384 | free(nrtm_q.source); 385 | return; 386 | } 387 | 388 | current_serial=nrtm_q.first; 389 | 390 | /* print banner */ 391 | { 392 | /* get the header string */ 393 | char *resp_header = ca_get_pw_resp_header; 394 | /* sprintf(buff, "\n%% Rights restricted by copyright. See http://www.ripe.net/ripencc/pub-services/db/copyright.html\n\n"); */ 395 | SK_cd_puts(&condat, "\n"); 396 | SK_cd_puts(&condat, resp_header); 397 | free(resp_header); 398 | SK_cd_puts(&condat, "\n"); 399 | } 400 | 401 | sprintf(buff, "%%START Version: %d %s %ld-%ld\n\n", nrtm_q.version, nrtm_q.source, nrtm_q.first, nrtm_q.last); 402 | SK_cd_puts(&condat, buff); 403 | 404 | /* make a record for thread accounting */ 405 | TA_setactivity(buff); 406 | 407 | /*************************** MAIN LOOP ****************************/ 408 | /* now start feeding client with data */ 409 | do { 410 | 411 | object=PM_get_serial_object(sql_connection, current_serial, &operation); 412 | if (operation == OP_ADD) SK_cd_puts(&condat, "ADD\n\n"); 413 | else SK_cd_puts(&condat, "DEL\n\n"); 414 | 415 | SK_cd_puts(&condat, object); 416 | 417 | SK_cd_puts(&condat, "\n"); 418 | 419 | free(object); 420 | current_serial++; 421 | 422 | /* for real-time mirroring we need some piece of code */ 423 | if(persistent_connection && (condat.rtc == 0) ) 424 | { 425 | while(((nrtm_q.last = PM_get_current_serial(sql_connection))<current_serial) 426 | && (CO_get_do_server()==1))sleep(1); 427 | } 428 | 429 | } /* do while there are more serials, connection was not reset and XXX do_server is on*/ 430 | while((current_serial<=nrtm_q.last) && (condat.rtc == 0) && (CO_get_do_server()==1)); 431 | /*******************************************************************/ 432 | 433 | sprintf(buff, "%%END %s\n\n\n", nrtm_q.source); 434 | SK_cd_puts(&condat, buff); 435 | 436 | ER_inf_va(FAC_PM, ASP_PM_INPUT,"[%s] -- <%s:%ld-%ld (%ld)> ", 437 | hostaddress, nrtm_q.source, nrtm_q.first, nrtm_q.last, nrtm_q.last-nrtm_q.first+1); 438 | 439 | /* make a record for thread accounting */ 440 | TA_delete(); 441 | 442 | /* close the connection to SQL server */ 443 | SQ_close_connection(sql_connection); 444 | /* Free the hostaddress */ 445 | free(hostaddress); 446 | free(nrtm_q.source); 447 | 448 | 449 | 450 | } /* PM_interact() */