Zesstra | 83f011d | 2016-04-24 20:09:05 +0200 | [diff] [blame^] | 1 | /* |
| 2 | |
| 3 | //TODO: implement retries |
| 4 | */ |
| 5 | |
| 6 | #define protected public |
| 7 | |
| 8 | #pragma strict_types, save_types, rtt_checks, pedantic |
| 9 | #pragma no_clone, no_shadow |
| 10 | |
| 11 | #include <tls.h> |
| 12 | #include <config.h> |
| 13 | #include <driver_info.h> |
| 14 | #include <lpctypes.h> |
| 15 | #include <strings.h> |
| 16 | |
| 17 | // data structures |
| 18 | struct host_s { |
| 19 | string name; |
| 20 | string key; |
| 21 | string ip; |
| 22 | int port; |
| 23 | int last_contact; |
| 24 | int first_contact; |
| 25 | int reputation; |
| 26 | string *services; |
| 27 | int im_version; |
| 28 | int ncttl; |
| 29 | int mtu; |
| 30 | mapping received; |
| 31 | }; |
| 32 | |
| 33 | struct fragment_s { |
| 34 | string header; |
| 35 | string buf; |
| 36 | }; |
| 37 | |
| 38 | struct packet_s { |
| 39 | int id; |
| 40 | int timestamp; |
| 41 | int pversion; |
| 42 | int pflags; |
| 43 | struct fragment_s *fragments; |
| 44 | string peername; |
| 45 | mapping data; |
| 46 | string buf; |
| 47 | }; |
| 48 | |
| 49 | struct service_s { |
| 50 | string name; |
| 51 | string obname; |
| 52 | string fun; |
| 53 | closure recv_cb; |
| 54 | }; |
| 55 | |
| 56 | struct request_s { |
| 57 | int id; |
| 58 | struct packet_s packet; |
| 59 | closure callback; |
| 60 | int timeout; |
| 61 | }; |
| 62 | |
| 63 | // Prototypes & Defines |
| 64 | protected void init(); |
| 65 | void ping_many_muds(string *muds); |
| 66 | protected int calc_ncttl(struct host_s p); |
| 67 | protected void send_helo(string name); |
| 68 | |
| 69 | // mud-compatibility layer - might implement efuns with lfuns if not available |
| 70 | // on the specific mud. |
| 71 | inherit __DIR__"inetd_compat"; |
| 72 | |
| 73 | // Mapping of known peers with their names as keys. |
| 74 | mapping peers = ([]); |
| 75 | |
| 76 | // our own offered services |
| 77 | mapping services = ([]); |
| 78 | |
| 79 | // our own host_s structure - it also contains our _private_ key. |
| 80 | struct host_s self = (<host_s>); |
| 81 | |
| 82 | // reset counter for housekeeping |
| 83 | int reset_counter; |
| 84 | |
| 85 | nosave int last_packet_id = 0; |
| 86 | nosave mapping requests = ([]); |
| 87 | |
| 88 | // Minimum protocol version to accept (default 0) |
| 89 | int min_protocol_version = 0; |
| 90 | |
| 91 | // *** lfuns in this section might be re-defined to suit the individual muds. |
| 92 | protected void debug_msg(string msg, int severity) |
| 93 | { |
| 94 | write(msg); |
| 95 | } |
| 96 | |
| 97 | protected void set_user() |
| 98 | { |
| 99 | seteuid(getuid()); |
| 100 | } |
| 101 | |
| 102 | protected void save_me() |
| 103 | { |
| 104 | } |
| 105 | |
| 106 | protected int restore_me() |
| 107 | { |
| 108 | } |
| 109 | |
| 110 | protected void export_peer_list(mapping list) |
| 111 | { |
| 112 | } |
| 113 | |
| 114 | // *** end of mud-specific lfuns *** |
| 115 | |
| 116 | private string *sys_fields = ({HOST, NAME, PACKET, UDP_PORT, SYSTEM, VERSION, |
| 117 | FLAGS, SIGNATURE, |
| 118 | PACKET_LOSS, RESPONSE_TIME}); |
| 119 | |
| 120 | // *** public interface |
| 121 | public void register_service(string name, string fun) |
| 122 | { |
| 123 | struct service_s serv; |
| 124 | |
| 125 | closure cl = symbol_function(fun, previous_object()); |
| 126 | if (!cl) |
| 127 | raise_error("register_service(): function not public.\n"); |
| 128 | |
| 129 | if (member(services, name)) |
| 130 | serv = services[name]; |
| 131 | else |
| 132 | serv = (<service_s>); |
| 133 | |
| 134 | serv->name = name; |
| 135 | serv->obname = object_name(previous_object()); |
| 136 | serv->fun = fun; |
| 137 | serv->recv_cb = cl; |
| 138 | services[name] = serv; |
| 139 | self->services = m_indices(services); |
| 140 | debug_msg(sprintf("Service registered: %O\n",serv),40); |
| 141 | } |
| 142 | |
| 143 | public void unregister_service(string name) |
| 144 | { |
| 145 | struct service_s serv = services[name]; |
| 146 | if (!structp(serv)) |
| 147 | return; |
| 148 | // wenn von aussen gerufen, nur registriertes Objekt selber. |
| 149 | if (extern_call() && serv->obname != object_name(previous_object())) |
| 150 | raise_error("Service can only be unregistered by service object.\n"); |
| 151 | m_delete(services, name); |
| 152 | self->services = m_indices(services); |
| 153 | debug_msg(sprintf("Service unregistered: %s\n",name),40); |
| 154 | } |
| 155 | |
| 156 | public string *QueryPeerList() |
| 157 | { |
| 158 | return m_indices(peers); |
| 159 | } |
| 160 | |
| 161 | // TODO: remove, for debug only |
| 162 | public struct host_s QueryPeer(string name) |
| 163 | { |
| 164 | return peers[name]; |
| 165 | } |
| 166 | |
| 167 | public string PeerName() |
| 168 | { |
| 169 | return self->name; |
| 170 | } |
| 171 | |
| 172 | // **************** internal implementation *********************** |
| 173 | |
| 174 | |
| 175 | protected void configure_host(struct host_s new_self) |
| 176 | { |
| 177 | new_self->first_contact = time(); |
| 178 | new_self->last_contact = time(); |
| 179 | new_self->reputation = __INT_MAX__; |
| 180 | new_self->ncttl = __INT_MAX__; |
| 181 | //if (!sizeof(new_self->services)) |
| 182 | // raise_error("Illegal to configure peer without services!\n"); |
| 183 | if(new_self->key) |
| 184 | new_self->im_version=2500; |
| 185 | else |
| 186 | new_self->im_version=2000; |
| 187 | if (new_self->mtu < 1024) |
| 188 | raise_error("The minimum UDP length (MTU) must >= 1024 bytes!\n"); |
| 189 | // ok, uebernehmen |
| 190 | self = new_self; |
| 191 | debug_msg(sprintf("localhost configured: %O\n",self),30); |
| 192 | } |
| 193 | |
| 194 | // Updates (or adds a new) peer. |
| 195 | protected struct host_s add_new_peer(struct host_s p) |
| 196 | { |
| 197 | // TODO: validate |
| 198 | // This must not happen, the caller must check for existence to prevent |
| 199 | // hijacking peer names... |
| 200 | if (member(peers, p->name)) |
| 201 | raise_error("Attempt to add existing peer!\n"); |
| 202 | if (p->mtu < 1024) p->mtu = 1024; |
| 203 | p->received = ([]); |
| 204 | peers[p->name] = p; |
| 205 | debug_msg(sprintf("new peer added: %O\n",p),50); |
| 206 | return p; |
| 207 | } |
| 208 | |
| 209 | // Updates (or adds a new) peer. |
| 210 | protected void update_peer(struct host_s p) |
| 211 | { |
| 212 | //TODO validate |
| 213 | struct host_s old = peers[p->name]; |
| 214 | if (!old) |
| 215 | peers[p->name] = p; |
| 216 | else |
| 217 | { |
| 218 | // we always keep some data... |
| 219 | old->key = p->key; |
| 220 | old->ip = p->ip; |
| 221 | old->port = p->port; |
| 222 | if (p->last_contact > old->last_contact) |
| 223 | old->last_contact = p->last_contact; |
| 224 | if (p->first_contact < old->first_contact) |
| 225 | old->first_contact = p->first_contact; |
| 226 | old->services=p->services; |
| 227 | old->reputation = p->reputation; |
| 228 | old->im_version = p->im_version; |
| 229 | if (p->mtu >= 1024) old->mtu = p->mtu; |
| 230 | old->ncttl = p->ncttl; |
| 231 | // keep packet fragments, don't update received. |
| 232 | debug_msg(sprintf("peer updated: %O\n",self),50); |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | // remove a peer from the known peer list |
| 237 | protected void kill_peer(string name) |
| 238 | { |
| 239 | m_delete(peers, name); |
| 240 | debug_msg(sprintf("peer deleteted: %s\n",name),50); |
| 241 | } |
| 242 | |
| 243 | // Update last_contact value to current time. |
| 244 | protected void touch_peer(string name) |
| 245 | { |
| 246 | struct host_s p = peers[name]; |
| 247 | if (p) |
| 248 | p->last_contact = time(); |
| 249 | } |
| 250 | |
| 251 | // imports old style host lists. Should only be done once. |
| 252 | public int import_host_list(string file) { |
| 253 | |
| 254 | int res; |
| 255 | mixed data = (read_file(file)); |
| 256 | if (!data) { |
| 257 | debug_msg("*Error in reading host file: " + file +"\n\n", 100); |
| 258 | return 0; |
| 259 | } |
| 260 | |
| 261 | data = old_explode(data, "\n"); |
| 262 | |
| 263 | foreach(string line: data) |
| 264 | { |
| 265 | if (line == "" || line[0] == '#') |
| 266 | continue; |
| 267 | mixed fields = old_explode(line, HOSTFILE_DEL); |
| 268 | if (sizeof(fields) < 5) { |
| 269 | debug_msg(sprintf( |
| 270 | "*Parse error in hosts file: %s, Line: %s\n", file, line), 100); |
| 271 | continue; |
| 272 | } |
| 273 | |
| 274 | struct host_s p = (<host_s>); |
| 275 | p->name = lower_case(fields[HOSTLIST_NAME]); |
| 276 | // skip already known peers |
| 277 | if (member(peers, p->name)) |
| 278 | continue; // mud already in list |
| 279 | |
| 280 | p->services = old_explode(fields[HOSTLIST_LOCAL_COMMANDS],HOSTFILE_DEL2); |
| 281 | if (member(p->services,"*") != -1) |
| 282 | p->services = p->services - ({ "*" }) + DEFAULT_COMMANDS; |
| 283 | p->ip = fields[HOSTLIST_IP]; |
| 284 | p->port = to_int(fields[HOSTLIST_UDP_PORT]); |
| 285 | // we fake first and last contact, otherwise it would be expired if not online. |
| 286 | p->first_contact = time(); |
| 287 | p->last_contact = time(); |
| 288 | p->mtu = 1024; |
| 289 | p->ncttl = calc_ncttl(p); |
| 290 | |
| 291 | add_new_peer(p); |
| 292 | ++res; |
| 293 | } |
| 294 | debug_msg(sprintf("old-stype host list imported: %s, %d new peers\n", |
| 295 | file,res),50); |
| 296 | return res; |
| 297 | } |
| 298 | |
| 299 | |
| 300 | protected void init() |
| 301 | { |
| 302 | if (!self) |
| 303 | raise_error("Can't init/startup without configuration!\n"); |
| 304 | |
| 305 | // Register the core services. Must be callother for previous_object being |
| 306 | // ourself |
| 307 | this_object()->register_service(PING, "recv_ping"); |
| 308 | this_object()->register_service(QUERY, "recv_query"); |
| 309 | this_object()->register_service(REPLY, "recv_reply"); |
| 310 | this_object()->register_service(HELO, "recv_helo"); |
| 311 | debug_msg(sprintf("init() performed, pinging known peers.\n"),30); |
| 312 | ping_many_muds(m_indices(peers)); |
| 313 | } |
| 314 | |
| 315 | protected string encode(mixed v) |
| 316 | { |
| 317 | switch(typeof(v)) |
| 318 | { |
| 319 | case T_NUMBER: |
| 320 | return to_string(v); |
| 321 | case T_STRING: |
| 322 | return "$"+v; |
| 323 | default: |
| 324 | // convert into a string representation - hopefully the receiver |
| 325 | // knows what to do with it. |
| 326 | return sprintf("$%Q",v); |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | #define SIGLENGTH 32+4 // length of signature including "$S:|" |
| 331 | |
| 332 | protected void sign_fragment(struct fragment_s f) |
| 333 | { |
| 334 | if (self->key) |
| 335 | { |
| 336 | f->header=sprintf("%s$S:%s|", f->header, |
| 337 | hmac(TLS_HASH_SHA512, self->name, f->buf)); |
| 338 | debug_msg(sprintf("signed fragment: %s, %s.\n",f->header,f->buf),90); |
| 339 | } |
| 340 | else |
| 341 | f->header=""; |
| 342 | } |
| 343 | |
| 344 | |
| 345 | // creates a packet structure with fragments to send (can be only one, if the |
| 346 | // packet does not need to be split). |
| 347 | protected struct packet_s packetize(struct host_s dest, mapping data) |
| 348 | { |
| 349 | // some field names in the packet are reserved and must not be used by |
| 350 | // users, they are added by the inetd. |
| 351 | if (sizeof(data & sys_fields)) |
| 352 | raise_error(sprintf( |
| 353 | "Data must not contain reserved system fields: %O.\n", |
| 354 | data & sys_fields)); |
| 355 | |
| 356 | // create packet structure |
| 357 | struct packet_s packet = (<packet_s> |
| 358 | id: ++last_packet_id, timestamp: time(), |
| 359 | pversion: self->im_version, pflags: 0, |
| 360 | fragments: 0, peername: 0, data: data, buf: 0); |
| 361 | |
| 362 | // add the system fields (NAME, V, F must be the first fields!) |
| 363 | // the ID field is special, because maybe the packet is an answer to a |
| 364 | // request. In this case, we must keep the original ID |
| 365 | if (!member(data, ID)) |
| 366 | data[ID] = packet->id; |
| 367 | else |
| 368 | { |
| 369 | if (data[REQUEST] != REPLY) |
| 370 | raise_error(sprintf("ID only permitted in reply packets!")); |
| 371 | } |
| 372 | // NAME is added below |
| 373 | data[UDP_PORT] = self->port; |
| 374 | |
| 375 | // create an array of fields (key:value pairs) |
| 376 | string data_field; |
| 377 | string * fields = allocate(sizeof(data)+3); |
| 378 | fields[0] = sprintf("NAME:%s", self->name); |
| 379 | fields[1] = sprintf("V:%d", packet->pversion); |
| 380 | fields[2] = sprintf("F:%d", packet->pflags); |
| 381 | int cindex = 3; |
| 382 | foreach(string key, mixed val : data) |
| 383 | { |
| 384 | // the DATA field must be added last, so remember its encoded string. |
| 385 | // Also, it is not checked for reserved characters |
| 386 | if (key == DATA) |
| 387 | data_field = sprintf("%s:%s", encode(DATA), encode(val)); |
| 388 | else |
| 389 | { |
| 390 | if (!stringp(key) || !sizeof(key)) |
| 391 | raise_error(sprintf("send((): Illegal field name %.30O in data " |
| 392 | "payload.\n",key)); |
| 393 | string ekey = encode(key); |
| 394 | string eval = encode(val); |
| 395 | // check key and value strings for reserved characters |
| 396 | if (regmatch(ekey,"[|:]") || regmatch(eval, "[|:]")) |
| 397 | raise_error(sprintf("send(): Field name or value contains reserved " |
| 398 | "character: %.50s,%.50s\n", ekey, eval)); |
| 399 | fields[cindex] = sprintf("%s:%s",ekey,eval); |
| 400 | ++cindex; |
| 401 | } |
| 402 | } |
| 403 | // add the DATA field if existing |
| 404 | if (data_field) |
| 405 | fields[cindex] = data_field; |
| 406 | |
| 407 | // write the string buffer |
| 408 | packet->buf = implode(fields, "|"); |
| 409 | |
| 410 | // now split into fragments if packet too large for one. |
| 411 | int mtu = min(self->mtu, dest->mtu); |
| 412 | if ((mtu - SIGLENGTH - sizeof(packet->buf)) < 0) |
| 413 | { |
| 414 | // multiple fragments, each starting with |
| 415 | // PKT:peername:packet-id:packet-number/total-packets| |
| 416 | // fsize is the maximum payload length per fragment. |
| 417 | string fheader = sprintf("PKT:%s:%d:", self->name, packet->id); |
| 418 | int fsize = mtu - SIGLENGTH - sizeof(fheader) |
| 419 | - 8; // sizeof("nnn/mmm|") |
| 420 | string buffer = packet->buf; |
| 421 | int fcount = sizeof(buffer) / fsize; |
| 422 | // if there is a modulo left, we need one additional fragment |
| 423 | if (sizeof(buffer) % fsize) |
| 424 | ++fcount; |
| 425 | if (fcount > 999) // too many fragments? |
| 426 | raise_error("packet too long to send.\n"); |
| 427 | // allocate the complete fragment array. |
| 428 | packet->fragments = allocate(fcount); |
| 429 | foreach(int i: fcount) |
| 430 | { |
| 431 | struct fragment_s f = (<fragment_s> |
| 432 | header:sprintf("%s%d/%d|",fheader,i+1,fcount), |
| 433 | buf: buffer[i*fsize .. min((i+1)*fsize, sizeof(buffer))-1] |
| 434 | ); |
| 435 | sign_fragment(f); |
| 436 | packet->fragments[i] = f; |
| 437 | debug_msg(sprintf("Created fragment %d/%d\n", i,fcount),90); |
| 438 | } |
| 439 | } |
| 440 | else |
| 441 | { |
| 442 | // one fragment, and the fragment header will be empty. |
| 443 | struct fragment_s f = (<fragment_s> header: "", |
| 444 | buf: packet->buf); |
| 445 | sign_fragment(f); |
| 446 | packet->fragments = ({f}); |
| 447 | debug_msg(sprintf("Created fragment 1/1\n"),90); |
| 448 | } |
| 449 | return packet; |
| 450 | } |
| 451 | |
| 452 | // encodes the mapping data into a packet and sends it to <dest>. The |
| 453 | // key-value pairs of <data> are converted into intermud fields. The keys must |
| 454 | // be strings and only the DATA field can contain : or |. |
| 455 | // Also, <data> must not contain any reserved field names. |
| 456 | // If the caller expects an answer, <cb> must contain a calleable closure. In |
| 457 | // this case, the inetd remembers the request. When the answer arrives, the |
| 458 | // callback <cb> is called with the request id (int) and a mapping (the answer |
| 459 | // packet) as arguments. The return value of send() is in this case the |
| 460 | // request id used for storage. |
| 461 | // <= 0 for error, > 0 for success. |
| 462 | public int send(string dest, mapping data, closure cb) |
| 463 | { |
| 464 | if (!sizeof(data)) |
| 465 | raise_error("Illegal sending empty packets."); |
| 466 | if (!sizeof(dest)) |
| 467 | raise_error("Can't send without destination: %O.\n"); |
| 468 | // find peer data. If not found, abort and tell the caller |
| 469 | struct host_s peer = peers[dest]; |
| 470 | if (!structp(peer)) |
| 471 | return -1; |
| 472 | |
| 473 | if (member(peer->services, data[REQUEST]) == -1) |
| 474 | raise_error("Remote host doesn't offer service "+ |
| 475 | data[REQUEST] + ".\n"); |
| 476 | |
| 477 | // then packetize the data, i.e. create packet structure, encode into buffer |
| 478 | // and split into fragments as needed and sign fragments. |
| 479 | // There will be at least one fragment structure. |
| 480 | struct packet_s packet = packetize(peer, data); |
| 481 | |
| 482 | // loop over all fragments and send them |
| 483 | foreach(struct fragment_s f : packet->fragments) |
| 484 | { |
| 485 | debug_msg(sprintf("%O <- %.500O\n",peer->ip, f->header + f->buf), 100); |
| 486 | // TODO: error handling |
| 487 | send_udp(peer->ip, peer->port, f->header + f->buf); |
| 488 | } |
| 489 | // delete the buffer |
| 490 | packet->buf = 0; |
| 491 | // delete the fragments until we support re-transmission of fragments |
| 492 | packet->fragments = 0; |
| 493 | |
| 494 | // we keep the original data for reference, if storing the request. |
| 495 | |
| 496 | // if an answer is expected, there is a callback closure. Then we store the |
| 497 | // request with a timeout of 120s. |
| 498 | if (cb) |
| 499 | { |
| 500 | struct request_s r = (<request_s> packet->id, packet, cb, time()+120); |
| 501 | requests[r->id] = r; |
| 502 | debug_msg(sprintf("Answer expected, request stored\n"),80); |
| 503 | } |
| 504 | |
| 505 | return packet->id; |
| 506 | } |
| 507 | |
| 508 | // like send(), but sends to all peers offering the service <service>. But |
| 509 | // limited to peers we had contact with in the last 24h. |
| 510 | public int broadcast(string service, mapping data, closure cb) |
| 511 | { |
| 512 | if (!service) |
| 513 | raise_error("No service given!\n"); |
| 514 | if (!sizeof(data)) |
| 515 | raise_error("Illegal sending empty packets."); |
| 516 | //TODO: check if we could send the same packet to all peers, instead of |
| 517 | //packetizing for each peer as well. |
| 518 | int ret; |
| 519 | int tlc_cutoff = time() - 86400; |
| 520 | foreach(string dest, struct host_s peer : peers) |
| 521 | { |
| 522 | if (peer->last_contact >= tlc_cutoff |
| 523 | && member(peer->service, service) != -1) |
| 524 | { |
| 525 | send(dest, data, cb); |
| 526 | ++ret; |
| 527 | } |
| 528 | } |
| 529 | return ret; |
| 530 | } |
| 531 | |
| 532 | protected string|int decode(string arg) |
| 533 | { |
| 534 | if (sizeof(arg) && arg[0] == '$') |
| 535 | return arg[1..]; |
| 536 | if (to_string(to_int(arg)) == arg) |
| 537 | return to_int(arg); |
| 538 | |
| 539 | return arg; |
| 540 | } |
| 541 | |
| 542 | // must be validated before. Parses the packet buffer into the given packet |
| 543 | // structure and returns it. In case of errors (e.g. malformed packets), 0 is |
| 544 | // returned. |
| 545 | protected struct packet_s parse_packet(struct packet_s packet) |
| 546 | { |
| 547 | string *fields = explode(packet->buf, "|"); |
| 548 | // The DATA field may contain any number of | which don't signify a field. |
| 549 | // Therefore counting | over-estimates the number of fields. To prevent |
| 550 | // excessive memory allocation, we limit the allocation to 32 fields. The |
| 551 | // mapping may still grow if there are really more fields. |
| 552 | packet->data = m_allocate(min(sizeof(fields),32)); |
| 553 | int cindex; |
| 554 | while (cindex < sizeof(fields)) |
| 555 | { |
| 556 | string header, info; |
| 557 | /// DATA fields can be denoted by a preceeding blank field :-/ |
| 558 | if (!sizeof(fields[cindex])) |
| 559 | { |
| 560 | ++cindex; |
| 561 | header = DATA; |
| 562 | //Test for illegal packet length (no DATA) |
| 563 | if (cindex >= sizeof(fields)) |
| 564 | return 0; |
| 565 | // take the first "field" of DATA (the rest is added below) |
| 566 | info = fields[cindex]; |
| 567 | } |
| 568 | else if (sscanf(fields[cindex], "%s:%s", header, info) != 2) |
| 569 | return 0; |
| 570 | header = decode(header); |
| 571 | if (header == DATA) |
| 572 | { |
| 573 | // add the rest of the packet and combine back into one string if |
| 574 | // necessary |
| 575 | if (cindex < sizeof(fields)-1) |
| 576 | info = implode(({info}) + fields[cindex+1 ..], "|"); |
| 577 | // and we have finished after decoding info |
| 578 | cindex = sizeof(fields); |
| 579 | } |
| 580 | packet->data[header] = decode(info); |
| 581 | ++cindex; |
| 582 | } |
| 583 | // only allow printable characters for these fields. |
| 584 | response[SENDER] = |
| 585 | regreplace(response[SENDER],"[:^print:]|\n","",1); |
| 586 | response[NAME] = |
| 587 | regreplace(response[NAME],"[:^print:]|\n","",1); |
| 588 | response[RECIPIENT] = |
| 589 | regreplace(response[RECIPIENT],"[:^print:]|\n","",1); |
| 590 | |
| 591 | return packet; |
| 592 | } |
| 593 | |
| 594 | protected void defragment_packet(struct packet_s p) |
| 595 | { |
| 596 | if (sizeof(p->fragments)) |
| 597 | { |
| 598 | p->buf = ""; |
| 599 | foreach(struct fragment_s f : p->fragments) |
| 600 | { |
| 601 | p->buf += f->buf; |
| 602 | } |
| 603 | p->fragments = 0; |
| 604 | } |
| 605 | } |
| 606 | |
| 607 | protected int validate_signature(string signature, string buf, struct host_s src) |
| 608 | { |
| 609 | if (!src->key) |
| 610 | return 0; |
| 611 | // TODO: debug purpose |
| 612 | return 1; |
| 613 | } |
| 614 | |
| 615 | // returns a packet_s or 0 for invalid or incomplete (!) packet. In both |
| 616 | // cases, the caller should not do anything. In case of incomplete packets, |
| 617 | // the fragment will be stored, if it was valid. |
| 618 | protected struct packet_s validate_fragment(string buffer, struct host_s sender) |
| 619 | { |
| 620 | string signature, pname; |
| 621 | int packetid, fragno, totalfrags, version, flags; |
| 622 | struct packet_s packet; |
| 623 | struct fragment_s f; |
| 624 | |
| 625 | if (sizeof(buffer) > MAX_UDP_LENGTH) |
| 626 | return 0; |
| 627 | |
| 628 | if (sscanf(buffer, "S:%s|NAME:%s|V:%d|$F:%d|%~s", |
| 629 | signature, pname, version, flags) == 5) |
| 630 | { |
| 631 | // non-fragmented v2.5+ packet |
| 632 | debug_msg(sprintf("Received v2.5+ packet.\n"),90); |
| 633 | pname = lower_case(pname); |
| 634 | packet = (<packet_s> timestamp: time(), pversion: version, |
| 635 | pflags: (flags & ~FL_VALID_SIGNATURE), |
| 636 | buf: buffer[strstr(buffer,"|")+1..] |
| 637 | ); |
| 638 | debug_msg(sprintf("Received v2.5+ packet from %s.\n",pname),90); |
| 639 | sender = peers[pname]; |
| 640 | // if we don't know the sender yet, we create a new peer with its name. |
| 641 | // The ip and port will be updated during parsing. |
| 642 | if (!sender) |
| 643 | { |
| 644 | sender = add_new_peer( (<host_s> name: pname, |
| 645 | first_contact: time(), |
| 646 | last_contact: time(), |
| 647 | mtu: 1024, |
| 648 | im_version : version, |
| 649 | received: m_allocate(5) |
| 650 | )); |
| 651 | // but if we can't create one, we discard the packet |
| 652 | if (!sender) |
| 653 | return 0; |
| 654 | } |
| 655 | packet->peername = pname; |
| 656 | // try to check the packet signature and record the result, if |
| 657 | // successful. |
| 658 | if (validate_signature(signature, packet->buf, sender)) |
| 659 | { |
| 660 | packet->pflags |= FL_VALID_SIGNATURE; |
| 661 | } |
| 662 | } |
| 663 | else if (sscanf(buffer, "PKT:%.1s:%d:%d/%d|%~s", |
| 664 | pname, packetid, fragno, totalfrags) == 5) |
| 665 | { |
| 666 | // this is a fragmented packet |
| 667 | // we at least check if the info about fragmentation is sane. |
| 668 | if (totalfrags > 999 || fragno > totalfrags |
| 669 | || totalfrags < 1 || fragno < 1) |
| 670 | return 0; |
| 671 | |
| 672 | pname=lower_case(pname); |
| 673 | |
| 674 | // is it even a IM 2.5+ packet? (want to avoid copying the buffer, |
| 675 | // therefore this approach) |
| 676 | if (sscanf(buffer, "PKT:%.1~s:%~d:%~d/%~d|$S:%.10s|%~s", |
| 677 | signature) == 6) |
| 678 | { |
| 679 | // create fragment (we know, there are at least two |) |
| 680 | int first_pipe = strstr(buffer,"|"); |
| 681 | int sig_end = strstr(buffer, "|", first_pipe+1); |
| 682 | f = (<fragment_s> header: buffer[0..first_pipe], |
| 683 | buf: buffer[sig_end+1 ..] |
| 684 | ); |
| 685 | debug_msg(sprintf("Received v2.5+ packet %d, fragment %d/%d from %s.\n" |
| 686 | ,packetid, fragno, totalfrags, pname),90); |
| 687 | } |
| 688 | else |
| 689 | { |
| 690 | // create fragment (we know, there is at least one |) |
| 691 | debug_msg(sprintf("Received v2 packet %d, fragment %d/%d from %s.\n" |
| 692 | ,packetid, fragno, totalfrags, pname),90); |
| 693 | int header_end = strstr(buffer, "|"); |
| 694 | f = (<fragment_s> header: buffer[0..header_end], |
| 695 | buf: buffer[header_end+1 ..] |
| 696 | ); |
| 697 | } |
| 698 | |
| 699 | // get the sender |
| 700 | sender = peers[pname]; |
| 701 | // if we don't know the sender yet, we create a new peer with its name. |
| 702 | // The ip and port will be updated during parsing, when the packet is |
| 703 | // complete. |
| 704 | if (!sender) |
| 705 | { |
| 706 | // if we can't create one, we discard the fragment |
| 707 | sender = add_new_peer( (<host_s> name: pname, |
| 708 | first_contact: time(), |
| 709 | last_contact: time(), |
| 710 | mtu: 1024, |
| 711 | received: m_allocate(5) |
| 712 | )); |
| 713 | if (!sender) |
| 714 | return 0; |
| 715 | } |
| 716 | // did we already received a fragment? - get its packet |
| 717 | packet = sender->received[packetid]; |
| 718 | // if not create new packet structure and add it to the receive queue of |
| 719 | // the peer |
| 720 | if (!packet) |
| 721 | { |
| 722 | packet = (<packet_s> id : packetid, timestamp: time(), |
| 723 | peername: pname, |
| 724 | fragments : allocate(totalfrags, 0) |
| 725 | ); |
| 726 | m_add(sender->received, packetid, packet); |
| 727 | } |
| 728 | |
| 729 | // now we can also check the signature, if there is one. |
| 730 | if (signature && |
| 731 | validate_signature(signature, f->header+f->buf, sender)) |
| 732 | { |
| 733 | packet->pflags |= FL_VALID_SIGNATURE; |
| 734 | } |
| 735 | else |
| 736 | { |
| 737 | // if there is no valid signature, but we received a fragment of this |
| 738 | // packet with valid signature, we discard the fragment. |
| 739 | if (packet->pflags & FL_VALID_SIGNATURE) |
| 740 | { |
| 741 | debug_msg(sprintf("Received fragment %d with invalid signature in " |
| 742 | "signed packet %d from %s. Discarding fragment.\n" |
| 743 | ,fragno, packetid, pname),40); |
| 744 | return 0; |
| 745 | } |
| 746 | } |
| 747 | |
| 748 | // add fragment to its slot - if we receive a fragment twice, the last one |
| 749 | // arriving wins. |
| 750 | packet->fragments[fragno] = f; |
| 751 | // if not all fragments are received, we end processing here and wait for |
| 752 | // more. |
| 753 | if (member(packet->fragments, 0)) |
| 754 | return 0; |
| 755 | } |
| 756 | else |
| 757 | { |
| 758 | // we assume a non-fragmented legacy packet, no real validation |
| 759 | // possible. |
| 760 | packet = (<packet_s> timestamp: time(), |
| 761 | buf: buffer |
| 762 | ); |
| 763 | |
| 764 | debug_msg(sprintf("Received v2 packet.\n"),90); |
| 765 | } |
| 766 | // at this point we have a complete packet - but we may have to defragment |
| 767 | // it yet. |
| 768 | if (packet->fragments) |
| 769 | { |
| 770 | defragment_packet(packet); |
| 771 | // and remove it from the receive queue of the sender (note, if we have |
| 772 | // fragments in the packet, we also have a valid sender at this point) |
| 773 | m_delete(sender->received, packet->id); |
| 774 | // and check for versions and flags - will be there for IM 2.5+ packets. |
| 775 | // Note: if the sender claims a version < 2000 here, this is also fine. |
| 776 | // If not there, we assume IM 2/Zebedee. |
| 777 | if (sscanf(buffer, "NAME:%~s|V:%d|$F:%d|%~s", |
| 778 | version, flags) == 4) |
| 779 | { |
| 780 | packet->pversion = version; |
| 781 | // store flags, but do not set the FL_VALID_SIGNATURE flag ;-) |
| 782 | packet->pflags |= (flags & ~FL_VALID_SIGNATURE); |
| 783 | } |
| 784 | else |
| 785 | packet->pversion = 2000; |
| 786 | } |
| 787 | |
| 788 | // if the packet has a version >= 2500, it must be signed. If not, we |
| 789 | // discard it. |
| 790 | if (packet->pversion >= 2500 |
| 791 | && !(packet->pflags & FL_VALID_SIGNATURE)) |
| 792 | { |
| 793 | // But there is one exception: if we don't have a key for the |
| 794 | // peer yet, we accept the packet anyway. This is needed to receive its |
| 795 | // public key, which we are going to ask them (soon). |
| 796 | if (sender->key) |
| 797 | { |
| 798 | debug_msg(sprintf("Received unsigned packet from v2.5+ peer with " |
| 799 | "key. Discarding."),40); |
| 800 | return 0; // key, but no valid signature - discard it. |
| 801 | } |
| 802 | } |
| 803 | |
| 804 | // Maybe we accept only packets conforming to a specific protocol version |
| 805 | // (or newer). This can be used to accept only signated packets. |
| 806 | // also we don't accept protocol version older than we already saw for this |
| 807 | // peer to prevent downgrade attacks |
| 808 | if (packet->pversion < min_protocol_version) |
| 809 | return 0; |
| 810 | |
| 811 | return packet; |
| 812 | } |
| 813 | |
| 814 | /* Check wether a UDP request was valid. Is done after validating and parsing |
| 815 | * the packet (and all its fragments). |
| 816 | * Logs are made and "host" information is updated as appropriate. |
| 817 | * Arguments: Decoded UDP packet (struct packet_s) |
| 818 | * sending peer (struct host_s) |
| 819 | * Returns: 0 for valid packets, an erro r string otherwise. |
| 820 | */ |
| 821 | string validate_request(struct packet_s p, struct host_s sender) |
| 822 | { |
| 823 | // If we have no peername, it was a legacy packet. Try to find the NAME in |
| 824 | // the packet payload. |
| 825 | if (!p->peername) |
| 826 | { |
| 827 | if(!member(p->data, NAME)) |
| 828 | return "Name of sending peer not given.\n"; |
| 829 | p->peername = lower_case(p->data[NAME]); |
| 830 | } |
| 831 | |
| 832 | if (p->peername == self->name) |
| 833 | return "Someone tried to fake ourself!\n"; |
| 834 | |
| 835 | // if needed, check the payload for the ID. The ID may be absent... |
| 836 | if (!p->id) |
| 837 | p->id = p->data[ID]; |
| 838 | // ... unless this is a reply to a request. And also, a reply can only be |
| 839 | // valid, when know about the initial request sent by us. |
| 840 | struct request_s req; |
| 841 | if (p->data[REQUEST] == REPLY) |
| 842 | { |
| 843 | if (!p->id) |
| 844 | return "Reply packet without packet ID.\n"; |
| 845 | // and we take note of the request while we are at it, in case we need it. |
| 846 | req = requests[p->id]; |
| 847 | if (!structp(req)) |
| 848 | return "Reply packet without request.\n"; |
| 849 | } |
| 850 | |
| 851 | // if we still have no sender (legacy non-fragmented packets), we finally |
| 852 | // create one, because we know the sender by name now. |
| 853 | sender = peers[p->peername]; |
| 854 | if (!sender) |
| 855 | { |
| 856 | // but if we can't create one, we discard the request |
| 857 | sender = add_new_peer( (<host_s> name: p->peername, |
| 858 | first_contact: time(), |
| 859 | last_contact: time(), |
| 860 | mtu: 1024, |
| 861 | received: m_allocate(3), |
| 862 | )); |
| 863 | if (!sender) |
| 864 | return "New peer, but peer list too full.\n"; |
| 865 | } |
| 866 | |
| 867 | // we don't accept protocol version older than we already saw for this |
| 868 | // peer to prevent downgrade attacks. |
| 869 | if (p->pversion < sender->im_version) |
| 870 | return "Downgraded peer intermud version."; |
| 871 | else |
| 872 | sender->im_version = p->pversion; |
| 873 | |
| 874 | // if we have a packet with version >= 2500 and no key for the peer |
| 875 | // yet, we ask them by sending a HELO packet (which also transmits our |
| 876 | // public key). |
| 877 | // Note: if we have a packet with version >= 2500, no valid signature, but |
| 878 | // already a key for the peer, validate_fragment() would already have |
| 879 | // discarded the packet/fragment and we would not be here. |
| 880 | // Of course, don't HELO, if this is a helo. |
| 881 | if (p->pversion >= 2500 && !sender->key |
| 882 | && p->data[REQUEST] != HELO) |
| 883 | { |
| 884 | // also don't HELO, if this is a response to a HELO... |
| 885 | if (!req || req->packet->data[REQUEST] != HELO) |
| 886 | send_helo(p->peername); |
| 887 | } |
| 888 | |
| 889 | // Note: HELO packets or HELO reponses in packets without valid signature |
| 890 | // can't survive until here if we know a key for the peer. Therefore, we |
| 891 | // don't have to validate that specifically. |
| 892 | |
| 893 | // TODO: e.g. some requests are allowed only in signed packets. |
| 894 | |
| 895 | return 0; // request ok! |
| 896 | } |
| 897 | |
| 898 | // processes the received packet or fragment. Must be called from the |
| 899 | // function called by the mudlib master in this object. |
| 900 | protected void process_fragment(string host, string msg, int hostport) |
| 901 | { |
| 902 | // Catch if someone tries to fake ourself. |
| 903 | if (host == self->ip && hostport == self->port) |
| 904 | return; |
| 905 | debug_msg(sprintf("Received packet from %s:%d.\n", |
| 906 | host, hostport), 100); |
| 907 | // First (try to) validate the received fragment (or packet). |
| 908 | struct host_s sender; |
| 909 | struct packet_s p = validate_fragment(msg, &sender); |
| 910 | // If we got a packet structure, the packet is complete and all the |
| 911 | // fragments were valid. If not, the packet is either invalid (and to be |
| 912 | // discarded) or not complete yet. In both cases, processing ends here. |
| 913 | if (!p) |
| 914 | return; |
| 915 | |
| 916 | // first parse the packet into a mapping. And again, when not successful, |
| 917 | // 0 will be returned. |
| 918 | p = parse_packet(p); |
| 919 | if (!p) |
| 920 | return; |
| 921 | |
| 922 | // record sending host data, may be important for validating the request. |
| 923 | p->data[HOST] = host; |
| 924 | p->data[UDP_PORT] = hostport; |
| 925 | |
| 926 | debug_msg(sprintf("Packet valid and complete.\n"), 90); |
| 927 | |
| 928 | // Validate the request... |
| 929 | string reason = validate_request(p, &sender); |
| 930 | if (reason) |
| 931 | { |
| 932 | debug_msg("validate_request(): discarding request: "+reason+"\n", 30); |
| 933 | return; //discard |
| 934 | } |
| 935 | sender->last_contact = time(); |
| 936 | // if we received the packet from a host/port combination that is different |
| 937 | // from the one we know for this peers, we update the ip address and port. |
| 938 | // Note: if the packet was signed, the origin is proved by now. If not... |
| 939 | // well... We believe it now. |
| 940 | if (sender->ip != host || sender->port != hostport) |
| 941 | { |
| 942 | debug_msg(sprintf("Updating address of peer %s from %s:%d to %s:%d.\n", |
| 943 | sender->name, sender->ip, sender->port, |
| 944 | host, hostport), 40); |
| 945 | sender->ip = host; |
| 946 | sender->port = hostport; |
| 947 | } |
| 948 | |
| 949 | touch_peer(sender->name); |
| 950 | |
| 951 | // then execute/forward it. |
| 952 | struct service_s srv = services[p->data[REQUEST]]; |
| 953 | if (srv) |
| 954 | { |
| 955 | if (!srv->recv_cb) |
| 956 | { |
| 957 | srv->recv_cb = symbol_function(srv->fun, find_object(srv->obname)); |
| 958 | if (!srv->recv_cb) |
| 959 | { |
| 960 | // If no callable, the service is unregistered. |
| 961 | unregister_service(srv->name); |
| 962 | return; |
| 963 | } |
| 964 | } |
| 965 | funcall(srv->recv_cb, p->data); |
| 966 | } |
| 967 | else |
| 968 | // unknown service. Discard for now. //TODO:: send reply |
| 969 | return; |
| 970 | |
| 971 | } // process_fragment() |
| 972 | |
| 973 | |
| 974 | // returns an increase in reputation (may be 0), |
| 975 | // may be overloaded in inheritees. |
| 976 | protected int auto_promote_peer(struct host_s p) |
| 977 | { |
| 978 | // only if we heard from that peer in the last day. |
| 979 | if (p->last_contact + 86400 < time()) |
| 980 | return 0; |
| 981 | // TODO: decide if auto-promotion above n should be tied to having a key. |
| 982 | if (p->reputation < 1 |
| 983 | && p->first_contact < time() - 7*86400) |
| 984 | return 1; |
| 985 | if (p->reputation < 2 |
| 986 | && p->first_contact < time() - 30*86400) |
| 987 | return 1; |
| 988 | if (p->reputation < 3 |
| 989 | && p->first_contact < time() - 12*30*86400) |
| 990 | return 1; |
| 991 | |
| 992 | return 0; |
| 993 | } |
| 994 | |
| 995 | // Calculate the not-connected-time-to-live depending on the reputation. May |
| 996 | // be overloaded in inheritees. |
| 997 | protected int calc_ncttl(struct host_s p) |
| 998 | { |
| 999 | switch(p->reputation) |
| 1000 | { |
| 1001 | case 0: |
| 1002 | return 3*86400; |
| 1003 | case 1: |
| 1004 | return 14*86400; |
| 1005 | case 2: |
| 1006 | return 3*30*86400; |
| 1007 | case 3: |
| 1008 | return 6*30*86400; |
| 1009 | default: |
| 1010 | return 12*30*86400; |
| 1011 | } |
| 1012 | } |
| 1013 | |
| 1014 | // checks if a peer should be expired (forgotten) and deletes it from our |
| 1015 | // known peer list if necessary. |
| 1016 | protected int check_and_delete_peer(struct host_s peer) |
| 1017 | { |
| 1018 | if (peer->last_contact + peer->ncttl < time()) |
| 1019 | { |
| 1020 | m_delete(peers, peer->name); |
| 1021 | } |
| 1022 | } |
| 1023 | |
| 1024 | void reset() |
| 1025 | { |
| 1026 | set_next_reset(600); |
| 1027 | // housekeeping of peer list every 60 resets (6h). |
| 1028 | reset_counter = ++reset_counter % 60; |
| 1029 | |
| 1030 | // expire stale requests |
| 1031 | foreach(int id, struct request_s r : requests) |
| 1032 | { |
| 1033 | if (r->timeout < time()) |
| 1034 | { |
| 1035 | // in case of timeouts we call the callback, but with no data |
| 1036 | funcall(r->callback, id, r->packet->data, 0); |
| 1037 | m_delete(requests, id); |
| 1038 | } |
| 1039 | } |
| 1040 | |
| 1041 | // peer housekeeping: increse reputation, expire peers |
| 1042 | // expire fragments in all peers |
| 1043 | foreach(string name, struct host_s peer : peers) |
| 1044 | { |
| 1045 | // should we do housekeeping of peer list? |
| 1046 | if (!reset_counter) |
| 1047 | { |
| 1048 | // first check if peer should be expired, because we had no contact for an |
| 1049 | // extended time. |
| 1050 | if (check_and_delete_peer(peer)) |
| 1051 | continue; |
| 1052 | // check if the reputation of a peer can be auto-promoted |
| 1053 | peer->reputation += auto_promote_peer(peer); |
| 1054 | peer->ncttl = calc_ncttl(peer); |
| 1055 | } |
| 1056 | |
| 1057 | if (!mappingp(peer->received) || !sizeof(peer->received)) |
| 1058 | continue; |
| 1059 | |
| 1060 | // check all incomplete fragments |
| 1061 | foreach(int id, struct packet_s pack : peer->received) |
| 1062 | { |
| 1063 | if (pack->timestamp + 120 < time()) |
| 1064 | m_delete(peer->received, id); |
| 1065 | //TODO: send info to sender? |
| 1066 | } |
| 1067 | } |
| 1068 | } |
| 1069 | |
| 1070 | // *** Core services every implementation must have. |
| 1071 | |
| 1072 | // we received a reply to one of our pings |
| 1073 | protected void recv_ping_reply(int id, mapping request, mapping response) |
| 1074 | { |
| 1075 | //TODO: what should we do? |
| 1076 | } |
| 1077 | |
| 1078 | // we received a ping request |
| 1079 | protected void recv_ping(mapping data) |
| 1080 | { |
| 1081 | send(data[NAME], ([ID: data[ID], REQUEST:REPLY, |
| 1082 | DATA: self->name +" is alive!\n"]), 0); |
| 1083 | } |
| 1084 | |
| 1085 | // sends a ping request to a peer |
| 1086 | protected void send_ping(string mud) |
| 1087 | { |
| 1088 | send(mud, ([ REQUEST: PING ]), #'recv_ping_reply); |
| 1089 | } |
| 1090 | |
| 1091 | // We received a reply for one of our QUERY requests. |
| 1092 | protected void recv_query_reply(int id, mapping request, mapping response) |
| 1093 | { |
| 1094 | //TODO: what do we do with the information? |
| 1095 | } |
| 1096 | |
| 1097 | // send a query request asking for <prop> |
| 1098 | public void send_query(string name, string prop) |
| 1099 | { |
| 1100 | if (!stringp(name) || !stringp(prop)) |
| 1101 | return; |
| 1102 | send(name, ([REQUEST: QUERY, DATA: prop, |
| 1103 | SENDER: getuid(previous_object()) ]), #'recv_query_reply); |
| 1104 | } |
| 1105 | |
| 1106 | // we received a reply request. |
| 1107 | protected void recv_query(mapping data) |
| 1108 | { |
| 1109 | mapping ret; |
| 1110 | switch(data[DATA]) |
| 1111 | { |
| 1112 | case "commands": |
| 1113 | ret = ([DATA: implode(self->services, ":") ]); |
| 1114 | break; |
| 1115 | case "email": |
| 1116 | ret = ([DATA: EMAIL]); |
| 1117 | break; |
| 1118 | case "hosts": |
| 1119 | string tmp=""; |
| 1120 | foreach(struct host_s p : peers) |
| 1121 | { |
| 1122 | tmp += p->name + ":" + p->ip + ":" + p->port |
| 1123 | + ":" + implode(p->services, ",") + ":" + |
| 1124 | implode(p->services, ",") + "\n"; |
| 1125 | } |
| 1126 | ret = ([DATA: trim(tmp, TRIM_RIGHT, "\n") ]); |
| 1127 | break; |
| 1128 | case "inetd": |
| 1129 | case "version": |
| 1130 | ret = ([DATA: self->im_version ]); |
| 1131 | break; |
| 1132 | case "list": |
| 1133 | ret = ([DATA: "commands,email,hosts,inetd,version,mud_port" ]); |
| 1134 | break; |
| 1135 | case "mud_port": |
| 1136 | ret = ([DATA: query_mud_port() ]); |
| 1137 | break; |
| 1138 | case "time": |
| 1139 | ret = ([DATA: time()]); |
| 1140 | break; |
| 1141 | default: |
| 1142 | return; // Just ignore request for the time being. |
| 1143 | } |
| 1144 | ret[REQUEST] = REPLY; |
| 1145 | ret[RECIPIENT] = data[SENDER]; |
| 1146 | ret[ID] = data[ID]; |
| 1147 | ret["QUERY"] = data[DATA]; //TODO: this is not right, right? |
| 1148 | |
| 1149 | send(data[NAME], ret, 0); |
| 1150 | } |
| 1151 | |
| 1152 | // Called, when we receive a reply of OUR HELO request. |
| 1153 | protected void recv_helo_reply(int id, mapping request, mapping response) |
| 1154 | { |
| 1155 | struct host_s peer = peers[response[NAME]]; |
| 1156 | // if we already have a key for the peer, we accept data and answer only if |
| 1157 | // the packet was correctly signed. Although the check should be redundant, |
| 1158 | // because then the packet should have been discarded earlier. But for this, |
| 1159 | // better safe, than sorry. |
| 1160 | if (peer->key |
| 1161 | && !(response[FLAGS] & FL_VALID_SIGNATURE)) |
| 1162 | raise_error("Unexpected unsigned packed from " |
| 1163 | + peer->name + ".\n"); |
| 1164 | |
| 1165 | mapping theirinfo = json_parse(response[DATA]); |
| 1166 | // yes - this is a way to update a key... Sending a HELO packet signed |
| 1167 | // with the old key |
| 1168 | peer->key = response["pkey"]; |
| 1169 | if (response["mtu"] >= 1024) |
| 1170 | peer->mtu = min(response["mtu"], MAX_UDP_LENGTH); |
| 1171 | if (pointerp(data["services"])) |
| 1172 | peer->services = data["services"]; |
| 1173 | } |
| 1174 | |
| 1175 | // we received a HELO packet, send back our info. |
| 1176 | protected void recv_helo(mapping data) |
| 1177 | { |
| 1178 | struct host_s peer = peers[data[NAME]]; |
| 1179 | // if we already have a key for the peer, we accept data and answer only if |
| 1180 | // the packet was correctly signed. Although the check should be redundant, |
| 1181 | // because then the packet should have been discarded earlier. But for this, |
| 1182 | // better safe, than sorry. |
| 1183 | if (peer->key |
| 1184 | && !(data[FLAGS] & FL_VALID_SIGNATURE)) |
| 1185 | raise_error("Unexpected unsigned packed from " |
| 1186 | + peer->name + ".\n"); |
| 1187 | |
| 1188 | mapping theirinfo = json_parse(data[DATA]); |
| 1189 | // yes - this is a way to update a key... Sending a HELO packet signed |
| 1190 | // with the old key |
| 1191 | peer->key = theirinfo["pkey"]; |
| 1192 | if (theirinfo["mtu"] >= 1024) |
| 1193 | peer->mtu = min(theirinfo["mtu"], MAX_UDP_LENGTH); |
| 1194 | if (pointerp(theirinfo["services"])) |
| 1195 | peer->services = theirinfo["services"]; |
| 1196 | |
| 1197 | mapping ourinfo = (["mtu": self->mtu, "pkey": self->key, |
| 1198 | "services": self->services ]); |
| 1199 | send(data[NAME], ([ID: data[ID], REQUEST:REPLY, |
| 1200 | DATA: json_serialize(ourinfo) ]), 0 ); |
| 1201 | } |
| 1202 | |
| 1203 | // send a HELO packet. |
| 1204 | protected void send_helo(string name) |
| 1205 | { |
| 1206 | mapping ourinfo = (["mtu": self->mtu, "pkey": self->key, |
| 1207 | "services": self->services]); |
| 1208 | send(name, ([REQUEST:HELO, |
| 1209 | DATA: json_serialize(ourinfo) ]), #'recv_helo_reply ); |
| 1210 | } |
| 1211 | |
| 1212 | // We received some reply for one of our requests - find our initial request |
| 1213 | // and forward the data to the callback. |
| 1214 | protected void recv_reply(mapping data) |
| 1215 | { |
| 1216 | // validate_request ensured that we have the initial request. |
| 1217 | int pid = data[ID]; |
| 1218 | debug_msg(sprintf("Received answer for request %d\n",pid),90); |
| 1219 | struct request_s req = requests[pid]; |
| 1220 | m_delete(requests, pid); |
| 1221 | funcall(req->callback, pid, req->packet->data, data); |
| 1222 | } |
| 1223 | |
| 1224 | /* |
| 1225 | * Make a PING request to all muds in the "hosts" mapping to set |
| 1226 | * HOSTLIST_STATUS information. |
| 1227 | * But don't ping all muds at once, because that may overflow the callout |
| 1228 | * table during mud startup, when hundreds of objects make callouts. |
| 1229 | */ |
| 1230 | void ping_many_muds(string *muds) |
| 1231 | { |
| 1232 | if (!pointerp(muds)) |
| 1233 | muds=m_indices(peers); |
| 1234 | if (!sizeof(muds)) |
| 1235 | return; |
| 1236 | string *part; |
| 1237 | if (sizeof(muds) > 9) |
| 1238 | part=muds[0..9]; |
| 1239 | else |
| 1240 | part=muds; |
| 1241 | foreach(string mud: part) |
| 1242 | send_ping(mud); |
| 1243 | muds -= part; |
| 1244 | if (sizeof(muds)) |
| 1245 | call_out(#'ping_many_muds, 4, muds); |
| 1246 | } |
| 1247 | |
| 1248 | protected void create() |
| 1249 | { |
| 1250 | if (object_name(this_object()) == __FILE__[0..<3]) |
| 1251 | { |
| 1252 | set_next_reset(-1); |
| 1253 | return; |
| 1254 | } |
| 1255 | set_user(); |
| 1256 | restore_me(); |
| 1257 | init(); |
| 1258 | } |
| 1259 | |
| 1260 | protected void create_super() |
| 1261 | { |
| 1262 | set_next_reset(-1); |
| 1263 | } |
| 1264 | |
| 1265 | int remove(int silent) |
| 1266 | { |
| 1267 | reset(); |
| 1268 | save_me(); |
| 1269 | destruct(this_object()); |
| 1270 | return 1; |
| 1271 | } |
| 1272 | |