1 | /*************************************** 2 | $Revision: 1.50 $ 3 | 4 | Protocol whois module (pw). Whois protocol. 5 | 6 | Status: NOT REVUED, TESTED 7 | 8 | ******************/ /****************** 9 | Filename : protocol_whois.c 10 | Authors : ottrey@ripe.net - framework and draft implementation 11 | marek@ripe.net - rewritten and extended. 12 | OSs Tested : Solaris 2.6 13 | ******************/ /****************** 14 | Copyright (c) 1999 RIPE NCC 15 | 16 | All Rights Reserved 17 | 18 | Permission to use, copy, modify, and distribute this software and its 19 | documentation for any purpose and without fee is hereby granted, 20 | provided that the above copyright notice appear in all copies and that 21 | both that copyright notice and this permission notice appear in 22 | supporting documentation, and that the name of the author not be 23 | used in advertising or publicity pertaining to distribution of the 24 | software without specific, written prior permission. 25 | 26 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 27 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL 28 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 29 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 30 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 31 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 32 | ***************************************/ 33 | #include <stdio.h> 34 | #include <glib.h> 35 | 36 | #include "NAME" 37 | 38 | #include "defs.h" 39 | #include "protocol_whois.h" 40 | #include "mysql_driver.h" 41 | #include "query_command.h" 42 | #include "query_instructions.h" 43 | #include "constants.h" 44 | 45 | #include "access_control.h" 46 | #include "sk.h" 47 | #include "stubs.h" 48 | 49 | #include "ca_configFns.h" 50 | #include "ca_macros.h" 51 | #include "ca_srcAttribs.h" 52 | 53 | #include "protocol_mirror.h" 54 | 55 | #include "ta.h" 56 | #include "timediff.h" 57 | 58 | #ifndef VERSION 59 | #define VERSION "3" 60 | #endif 61 | 62 | /*++++++++++++++++++++++++++++++++++++++ 63 | 64 | void 65 | display_file opens a file and displays its contents to the 66 | connection described in conn. structure. 67 | 68 | 69 | sk_conn_st *condat pointer to connection structure 70 | 71 | char *filename file name 72 | 73 | ++++++++++++++++++++++++++++++++++++++*/ 74 | static void 75 | display_file(sk_conn_st *condat, char *filename) 76 | { 77 | FILE *fp; 78 | #define READBUFSIZE 148 79 | char buffer[READBUFSIZE+1]; 80 | int bytes; 81 | 82 | if( (fp=fopen( filename, "r" )) == NULL ) { 83 | ER_perror( FAC_PW, PW_CNTOPN, "%s : %s (%d)", 84 | filename, strerror(errno), errno); 85 | } 86 | else { 87 | while( (bytes=fread(buffer, 1, READBUFSIZE, fp)) > 0 ) { 88 | buffer[bytes] = 0; 89 | SK_cd_puts(condat, buffer); 90 | } 91 | fclose(fp); 92 | } 93 | }/* display_file */ 94 | 95 | 96 | /*++++++++++++++++++++++++++++++++++++++ 97 | 98 | static void 99 | pw_log_query logs the query to a file after it has finished. 100 | Takes many parameters to have access to as much 101 | information as possible, including the original 102 | query, accounting, response time, status of the 103 | client connection, etc. 104 | 105 | 106 | Query_environ *qe query environment 107 | 108 | Query_command *qc query command structure 109 | 110 | acc_st *copy_credit numbers of objects returned / referrals made 111 | during this query 112 | (calculated as original credit assigned before 113 | the query minus what's left after the query). 114 | 115 | ut_timer_t begintime time the processing began 116 | 117 | ut_timer_t endtime time the processing finished 118 | 119 | char *hostaddress text address of the real IP 120 | 121 | char *input original query (trailing whitespaces chopped off) 122 | 123 | ++++++++++++++++++++++++++++++++++++++*/ 124 | static 125 | void pw_log_query( Query_environ *qe, 126 | Query_command *qc, 127 | acc_st *copy_credit, 128 | ut_timer_t begintime, 129 | ut_timer_t endtime, 130 | char *hostaddress, 131 | char *input) 132 | { 133 | char *qrystat = AC_credit_to_string(copy_credit); 134 | float elapsed; 135 | char *qrytypestr = 136 | qc->query_type == QC_REAL ? "" : QC_get_qrytype(qc->query_type); 137 | 138 | 139 | elapsed = UT_timediff( &begintime, &endtime); 140 | 141 | /* log the connection/query/#results/time/denial to file */ 142 | ER_inf_va(FAC_PW, ASP_PW_I_QRYLOG, 143 | "<%s> %s%s %.2fs [%s] -- %s", 144 | qrystat, 145 | qe->condat.rtc ? "INT " : "", 146 | qrytypestr, 147 | elapsed, hostaddress, input 148 | ); 149 | wr_free(qrystat); 150 | } /* pw_log_query */ 151 | 152 | 153 | 154 | 155 | /*++++++++++++++++++++++++++++++++++++++ 156 | 157 | void 158 | PW_process_qc processes the query commands determined in QC, 159 | This is where all the real action of the query 160 | part is invoked. 161 | 162 | Query_environ *qe query environment 163 | 164 | Query_command *qc query command structure 165 | 166 | acc_st *acc_credit credit assigned to this IP 167 | 168 | acl_st *acl_eip current acl record applicable to this IP 169 | 170 | ++++++++++++++++++++++++++++++++++++++*/ 171 | void PW_process_qc(Query_environ *qe, 172 | Query_command *qc, 173 | acc_st *acc_credit, 174 | acl_st *acl_eip ) 175 | { 176 | GList *qitem; 177 | Query_instructions *qis=NULL; 178 | er_ret_t err; 179 | 180 | switch( qc->query_type ) { 181 | case QC_SYNERR: 182 | SK_cd_puts(&(qe->condat), USAGE); 183 | /* FALLTHROUGH */ 184 | case QC_PARERR: 185 | /* parameter error. relevant error message is already printed */ 186 | 187 | /* force disconnection on error */ 188 | qe->k = 0; 189 | break; 190 | case QC_NOKEY: 191 | /* no key (this is OK for some operational stuff, like -k) */ 192 | break; 193 | case QC_EMPTY: 194 | /* The user didn't specify a key, so 195 | - print moron banner 196 | - force disconnection of the user. */ 197 | { 198 | char *rep = ca_get_pw_err_nokey ; 199 | SK_cd_puts(&(qe->condat), rep); 200 | wr_free(rep); 201 | } 202 | qe->condat.rtc = SK_NOTEXT; 203 | break; 204 | case QC_HELP: 205 | { 206 | char *rep = ca_get_pw_help_file ; 207 | display_file( &(qe->condat), rep); 208 | wr_free(rep); 209 | } 210 | break; 211 | case QC_TEMPLATE: 212 | switch(qc->q) { 213 | case QC_Q_SOURCES: 214 | /* print source & mirroring info */ 215 | { 216 | GString *srcs = PM_get_nrtm_sources( & qe->condat.rIP, NULL); 217 | SK_cd_puts(&(qe->condat), srcs->str); 218 | g_string_free (srcs, TRUE); 219 | } 220 | break; 221 | case QC_Q_VERSION: 222 | SK_cd_puts(&(qe->condat), "% RIP version " VERSION "\n\n"); 223 | break; 224 | default: 225 | /* EMPTY */; 226 | } /* -q */ 227 | 228 | if (qc->t >= 0) { 229 | SK_cd_puts(&(qe->condat), DF_get_class_template(qc->t)); 230 | } 231 | if (qc->v >= 0) { 232 | SK_cd_puts(&(qe->condat), DF_get_class_template_v(qc->v)); 233 | } 234 | break; 235 | 236 | case QC_FILTERED: 237 | { 238 | char *rep = ca_get_pw_k_filter ; 239 | SK_cd_puts(&(qe->condat), rep); 240 | wr_free(rep); 241 | } 242 | /* FALLTROUGH */ 243 | case QC_REAL: 244 | { 245 | char *rep = ca_get_pw_resp_header; 246 | SK_cd_puts(&(qe->condat), rep); 247 | wr_free(rep); 248 | SK_cd_puts(&(qe->condat), "\n"); 249 | } 250 | 251 | #if 1 252 | 253 | qis = QI_new(qc,qe); 254 | 255 | /* go through all sources, 256 | stop if connection broken - further action is meaningless */ 257 | for( qitem = g_list_first(qe->sources_list); 258 | qitem != NULL && qe->condat.rtc == 0; 259 | qitem = g_list_next(qitem)) { 260 | 261 | 262 | /* QI will decrement the credit counters */ 263 | err = QI_execute(qitem->data, qis, qe, acc_credit, acl_eip ); 264 | if( !NOERR(err) ) { 265 | if( err == QI_CANTDB ) { 266 | SK_cd_puts(&(qe->condat), "% WARNING: Failed to make connection to "); 267 | SK_cd_puts(&(qe->condat), (char *)qitem->data); 268 | SK_cd_puts(&(qe->condat), " database.\n\n"); 269 | } 270 | break; /* quit the loop after any error */ 271 | }/* if error*/ 272 | 273 | }/* for every source */ 274 | 275 | QI_free(qis); 276 | 277 | #else 278 | /* test mode: do not run a query, make up some accounting values */ 279 | { 280 | int i, m = random() & 0x0f; 281 | for( i=0 ; i<m ; i++ ) { 282 | AC_count_object( acc_credit, acl_eip, random() & 0x01 ); 283 | } 284 | } 285 | 286 | #endif 287 | 288 | if( AC_credit_isdenied(acc_credit) ) { 289 | /* host reached the limit of returned contact information */ 290 | char *rep = ca_get_pw_limit_reached ; 291 | SK_cd_puts(&(qe->condat), rep); 292 | wr_free(rep); 293 | } 294 | 295 | break; 296 | default: die; 297 | } 298 | } /* PW_process_qc */ 299 | 300 | 301 | 302 | 303 | /*++++++++++++++++++++++++++++++++++++++ 304 | 305 | void 306 | PW_interact Main loop for interaction with a single client. 307 | The function sets up the accounting for the client, 308 | invokes parsing, execution, logging and accounting 309 | of the query. 310 | 311 | int sock Socket that client is connected to. 312 | 313 | ++++++++++++++++++++++++++++++++++++++*/ 314 | void PW_interact(int sock) { 315 | char input[MAX_INPUT_SIZE]; 316 | int read_result; 317 | char *hostaddress=NULL; 318 | acl_st acl_rip, acl_eip; 319 | acc_st acc_credit, copy_credit; 320 | Query_environ *qe=NULL; 321 | Query_command *qc=NULL; 322 | ut_timer_t begintime, endtime; 323 | 324 | /* Get the IP of the client */ 325 | hostaddress = SK_getpeername(sock); 326 | ER_dbg_va(FAC_PW, ASP_PW_CONN, "connection from %s", hostaddress); 327 | 328 | /* Initialize the query environment. */ 329 | qe = QC_environ_new(hostaddress, sock); 330 | 331 | /* init the connection structure, set timeout for reading the query */ 332 | SK_cd_make( &(qe->condat), sock, (unsigned) ca_get_keepopen); 333 | 334 | TA_setcondat(&(qe->condat)); 335 | 336 | /* see if we should be talking at all */ 337 | /* check the acl using the realIP, get a copy applicable to this IP */ 338 | AC_check_acl( &(qe->condat.rIP), NULL, &acl_rip); 339 | 340 | do { 341 | int unauth_pass=0; 342 | 343 | TA_setactivity("waiting for query"); 344 | /* Read input */ 345 | read_result = SK_cd_gets(&(qe->condat), input, MAX_INPUT_SIZE); 346 | /* trash trailing whitespaces(including \n) */ 347 | ut_string_chop(input); 348 | 349 | TA_setactivity(input); 350 | TA_increment(); 351 | 352 | UT_timeget( &begintime ); 353 | 354 | qc = QC_create(input, qe); 355 | 356 | { 357 | /* print the greeting text before the query */ 358 | char *rep = ca_get_pw_banner ; 359 | SK_cd_puts(&(qe->condat), rep); 360 | wr_free(rep); 361 | SK_cd_puts(&(qe->condat), "\n"); 362 | } 363 | 364 | /* ADDRESS PASSING: check if -V option has passed IP in it */ 365 | if( ! STRUCT_EQUAL(qe->pIP,IP_ADDR_UNSPEC)) { 366 | if(acl_rip.trustpass) { 367 | acc_st pass_acc; 368 | 369 | /* accounting */ 370 | memset(&pass_acc, 0, sizeof(acc_st)); 371 | pass_acc.addrpasses=1; 372 | AC_commit( &qe->condat.rIP, &pass_acc, &acl_rip); 373 | 374 | /* set eIP to this IP */ 375 | qe->condat.eIP = qe->pIP; 376 | } 377 | else { 378 | /* XXX shall we deny such user ? Now we can... */ 379 | ER_inf_va(FAC_PW, ASP_PW_I_PASSUN, 380 | "unauthorised address passing by %s", hostaddress); 381 | unauth_pass = 1; /* keep in mind ... */ 382 | } 383 | } /* if an address was passed */ 384 | 385 | /* start setting counters in the connection acc from here on 386 | decrement the credit counter (needed to prevent QI_execute from 387 | returning too many results */ 388 | 389 | /* check ACL. Get the proper acl record. Calculate credit */ 390 | AC_check_acl( &(qe->condat.eIP), &acc_credit, &acl_eip); 391 | /* save the original credit, later check how much was used */ 392 | copy_credit = acc_credit; 393 | 394 | copy_credit.connections ++; 395 | 396 | /* printing notices */ 397 | if( unauth_pass && ! acl_rip.deny ) { 398 | /* host not authorised to pass addresses with -V */ 399 | char *rep = ca_get_pw_acl_addrpass ; 400 | SK_cd_puts(&(qe->condat), rep); 401 | wr_free(rep); 402 | } 403 | if( acl_eip.deny || acl_rip.deny ) { 404 | /* access from host has been permanently denied */ 405 | char *rep = ca_get_pw_acl_permdeny ; 406 | SK_cd_puts(&(qe->condat), rep); 407 | wr_free(rep); 408 | } 409 | 410 | if( acl_eip.deny || acl_rip.deny || unauth_pass ) { 411 | copy_credit.denials ++; 412 | } 413 | else { 414 | /************ ACTUAL PROCESSING IS HERE ***********/ 415 | PW_process_qc(qe, qc, &acc_credit, &acl_eip); 416 | 417 | if( qc->query_type == QC_REAL ) { 418 | copy_credit.queries ++; 419 | } 420 | }/* if denied ... else */ 421 | 422 | /* calc. the credit used, result into copy_credit 423 | This step MUST NOT be forgotten. It must complement 424 | the initial calculation of a credit, otherwise accounting 425 | will go bgzzzzzt. 426 | */ 427 | AC_acc_addup(©_credit, &acc_credit, ACC_MINUS); 428 | 429 | /* now we can check how many results there were, etc. */ 430 | 431 | /* can say 'nothing found' only if: 432 | - the query did not just cause denial 433 | - was a 'real' query 434 | - nothing was returned 435 | */ 436 | 437 | if( ! AC_credit_isdenied(©_credit) 438 | && (qc->query_type == QC_REAL || qc->query_type == QC_FILTERED) 439 | && copy_credit.private_objects + copy_credit.public_objects 440 | + copy_credit.referrals == 0 ) { 441 | 442 | /* now: if the rtc flag is zero, the query ran to completion */ 443 | if( qe->condat.rtc == 0 ) { 444 | char *rep = ca_get_pw_notfound ; 445 | SK_cd_puts(&(qe->condat), rep); 446 | wr_free(rep); 447 | } 448 | else { 449 | /* something happened. Hope for working socket and display message 450 | (won't hurt even if socket not operable) 451 | */ 452 | char *rep = ca_get_pw_connclosed ; 453 | SK_cd_puts(&(qe->condat), rep); 454 | wr_free(rep); 455 | } 456 | } 457 | 458 | 459 | UT_timeget(&endtime); 460 | /* query logging */ 461 | pw_log_query(qe, qc, ©_credit, begintime, endtime, 462 | hostaddress, input); 463 | 464 | /* Commit the credit. This will deny if bonus limit hit 465 | and clear the copy */ 466 | AC_commit(&(qe->condat.eIP), ©_credit, &acl_eip); 467 | 468 | /* end-of-result -> two empty lines */ 469 | SK_cd_puts(&(qe->condat), "\n\n"); 470 | 471 | QC_free(qc); 472 | } /* do */ 473 | while( qe->k && qe->condat.rtc == 0 474 | && AC_credit_isdenied( ©_credit ) == 0 475 | && CO_get_whois_suspended() == 0); 476 | 477 | /* Free the hostaddress */ 478 | wr_free(hostaddress); 479 | /* Free the connection struct's dynamic data */ 480 | SK_cd_free(&(qe->condat)); 481 | /* Free the query_environ */ 482 | QC_environ_free(qe); 483 | 484 | } /* PW_interact() */