mixmaster

mixmaster 3.0 patched for libressl
git clone git://parazyd.org/mixmaster.git
Log | Files | Refs | README

chain2.c (20338B)


      1 /* Mixmaster version 3.0  --  (C) 1999 - 2006 Anonymizer Inc. and others.
      2 
      3    Mixmaster may be redistributed and modified under certain conditions.
      4    This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
      5    ANY KIND, either express or implied. See the file COPYRIGHT for
      6    details.
      7 
      8    Encrypt message for Mixmaster chain
      9    $Id: chain2.c 934 2006-06-24 13:40:39Z rabbi $ */
     10 
     11 
     12 #include "mix3.h"
     13 #include <string.h>
     14 #include <stdlib.h>
     15 #include <time.h>
     16 #include <ctype.h>
     17 #include <assert.h>
     18 
     19 #define N(X) (isdigit(X) ? (X)-'0' : 0)
     20 
     21 int prepare_type2list(BUFFER *out)
     22 {
     23   FILE *list;
     24   char line[LINELEN], name[LINELEN], addr[LINELEN], keyid[LINELEN],
     25   version[LINELEN], flags[LINELEN], createdstr[LINELEN], expiresstr[LINELEN];
     26   int assigned;
     27   time_t created, expires;
     28 
     29   list = mix_openfile(PUBRING, "r");
     30   if (list == NULL)
     31     return (-1);
     32   while (fgets(line, sizeof(line), list) != NULL) {
     33     if (strleft(line, begin_key)) {
     34       while (fgets(line, sizeof(line), list) != NULL &&
     35 	     !strleft(line, end_key)) ;
     36     } else if (strlen(line) > 36 && line[0] != '#') {
     37       assigned = sscanf(line, "%127s %127s %127s %127s %127s %127s %127s",
     38 		 name, addr, keyid, version, flags, createdstr, expiresstr);
     39       if (assigned < 4)
     40 	continue;
     41       if (assigned >= 6) {
     42 	created = parse_yearmonthday(createdstr);
     43 	if (created == 0 || created == -1) {
     44 	  errlog(WARNING, "Cannot parse creation date of key %s.\n", keyid);
     45 	  continue;
     46 	};
     47 	if (created > time(NULL)) {
     48 	  errlog(WARNING, "Key %s created in the future.\n", keyid);
     49 	  continue;
     50 	};
     51       }
     52       if (assigned >= 7) {
     53 	expires = parse_yearmonthday(expiresstr);
     54 	if (expires == 0 || expires == -1) {
     55 	  errlog(WARNING, "Cannot parse expiration date of key %s.\n", keyid);
     56 	  continue;
     57 	};
     58 	if (expires < time(NULL)) {
     59 	  errlog(WARNING, "Key %s has expired.\n", keyid);
     60 	  continue;
     61 	};
     62       }
     63       buf_appends(out, line);
     64     }
     65   }
     66   fclose(list);
     67   return (0);
     68 }
     69 
     70 int mix2_rlist(REMAILER remailer[], int badchains[MAXREM][MAXREM])
     71 {
     72   FILE *list, *excl;
     73   int n, i, listed = 0;
     74 
     75   char line[LINELEN], name[LINELEN], addr[LINELEN], keyid[LINELEN],
     76   version[LINELEN], flags[LINELEN], createdstr[LINELEN], expiresstr[LINELEN];
     77   int assigned;
     78   time_t created, expires;
     79   BUFFER *starex;
     80 
     81   starex = buf_new();
     82   excl = mix_openfile(STAREX, "r");
     83   if (excl != NULL) {
     84     buf_read(starex, excl);
     85     fclose(excl);
     86   }
     87 
     88   list = mix_openfile(PUBRING, "r");
     89   if (list == NULL) {
     90     buf_free(starex);
     91     return (-1);
     92   }
     93   for (n = 1; fgets(line, sizeof(line), list) != NULL && n < MAXREM;)
     94     if (strleft(line, begin_key)) {
     95       while (fgets(line, sizeof(line), list) != NULL &&
     96 	     !strleft(line, end_key)) ;
     97     } else if (strlen(line) > 36 && line[0] != '#') {
     98       flags[0] = '\0';
     99       assigned = sscanf(line, "%127s %127s %127s %127s %127s %127s %127s",
    100 		 name, addr, keyid, version, flags, createdstr, expiresstr);
    101       if (assigned < 4)
    102 	continue;
    103       if (assigned >= 6) {
    104 	created = parse_yearmonthday(createdstr);
    105 	if (created == 0 || created == -1) {
    106 	  errlog(WARNING, "Cannot parse creation date of key %s.\n", keyid);
    107 	  continue;
    108 	};
    109 	if (created > time(NULL)) {
    110 	  errlog(WARNING, "Key %s created in the future.\n", keyid);
    111 	  continue;
    112 	};
    113       }
    114       if (assigned >= 7) {
    115 	expires = parse_yearmonthday(expiresstr);
    116 	if (expires == 0 || expires == -1) {
    117 	  errlog(WARNING, "Cannot parse expiration date of key %s.\n", keyid);
    118 	  continue;
    119 	};
    120 	if (expires < time(NULL)) {
    121 	  errlog(WARNING, "Key %s has expired.\n", keyid);
    122 	  continue;
    123 	};
    124       }
    125       strncpy(remailer[n].name, name, sizeof(remailer[n].name));
    126       remailer[n].name[sizeof(remailer[n].name) - 1] = '\0';
    127       strncpy(remailer[n].addr, addr, sizeof(remailer[n].addr));
    128       remailer[n].addr[sizeof(remailer[n].addr) - 1] = '\0';
    129       remailer[n].flags.mix = 1;
    130       remailer[n].flags.cpunk = 0;
    131       remailer[n].flags.nym = 0;
    132       remailer[n].flags.newnym = 0;
    133       id_decode(keyid, remailer[n].keyid);
    134       remailer[n].version = N(version[0]);
    135       remailer[n].flags.compress = strfind(flags, "C");
    136       remailer[n].flags.post = strfind(flags, "N");
    137       remailer[n].flags.middle = strfind(flags, "M");
    138       remailer[n].info[0].reliability = 0;
    139       remailer[n].info[0].latency = 0;
    140       remailer[n].info[0].history[0] = '\0';
    141       remailer[n].flags.star_ex = bufifind(starex, name);
    142       n++;
    143     }
    144   fclose(list);
    145   list = mix_openfile(TYPE2REL, "r");
    146   if (list != NULL) {
    147     while (fgets(line, sizeof(line), list) != NULL &&
    148 	   !strleft(line, "--------------------------------------------")) {
    149       if (strleft(line, "Last update:")) {
    150         int generated;
    151 	int now = time(NULL);
    152 	char *tmp = line + strlen("Last update:") + 1;
    153 	generated = parsedate(tmp);
    154 	if (generated == -1) {
    155 	  /* For some weird reason, this isn't rfc822 */
    156 	  if (strleft(tmp, "Mon") ||
    157 	      strleft(tmp, "Tue") ||
    158 	      strleft(tmp, "Wed") ||
    159 	      strleft(tmp, "Thu") ||
    160 	      strleft(tmp, "Fri") ||
    161 	      strleft(tmp, "Sat") ||
    162 	      strleft(tmp, "Sun"))
    163 	    tmp += 3;
    164           generated = parsedate(tmp);
    165 	}
    166 	now = time(NULL);
    167 	if (generated != -1 && generated < now - SECONDSPERDAY)
    168 	  errlog(WARNING, "Remailer Reliability Statistics are older than one day (check your clock?).\n");
    169 	if (generated != -1 && generated > now)
    170 	  errlog(WARNING, "Remailer Reliability Statistics are from the future (check your clock?).\n");
    171       }
    172     };
    173     while (fgets(line, sizeof(line), list) != NULL &&
    174 	   !strleft(line, "</PRE>"))
    175       if (strlen(line) >= 44 && strlen(line) <= 46)
    176 	for (i = 1; i < n; i++)
    177 	  if (strleft(line, remailer[i].name) &&
    178 	      line[strlen(remailer[i].name)] == ' ') {
    179 	    strncpy(remailer[i].info[0].history, line + 15, 12);
    180 	    remailer[i].info[0].history[12] = '\0';
    181 	    remailer[i].info[0].reliability = 10000 * N(line[37])
    182 	      + 1000 * N(line[38]) + 100 * N(line[39])
    183 	      + 10 * N(line[41]) + N(line[42]);
    184 	    remailer[i].info[0].latency = 36000 * N(line[28])
    185 	      + 3600 * N(line[29]) + 600 * N(line[31])
    186 	      + 60 * N(line[32]) + 10 * N(line[34])
    187 	      + N(line[35]);
    188 	    listed++;
    189 	  }
    190     fclose(list);
    191   }
    192 
    193   parse_badchains(badchains, TYPE2REL, "Broken type-II remailer chains", remailer, n);
    194   if (listed < 4)		/* we have no valid reliability info */
    195     for (i = 1; i < n; i++)
    196       remailer[i].info[0].reliability = 10000;
    197   buf_free(starex);
    198   return (n);
    199 }
    200 
    201 static int send_packet(int numcopies, BUFFER *packet, int chain[],
    202 		       int chainlen, int packetnum, int numpackets,
    203 		       BUFFER *mid, REMAILER remailer[], int badchains[MAXREM][MAXREM],
    204 		       int maxrem, char *redirect_to, int ignore_constraints_if_necessary,
    205 		       BUFFER *feedback)
    206 /*
    207  * Puts a mix packet in the pool.
    208  *
    209  * numcopies   ... how often to put this packet into the pool
    210  *                 i.e. send it.  required that random remailers are in the chain.
    211  * packet      ... the payload, 10240 bytes in size.
    212  * chain       ... the chain to send this message along
    213  * chainlen    ... length of the chain
    214  * packetnum   ... in multi-packet messages (fragmented) the serial of this packet
    215  * numpackets  ...  the total number of packets
    216  * mid         ... the message ID (required for fragmented packets
    217  * remailer    ... information about remailers, their reliabilities, capabilities, etc.
    218  * badchains   ... broken chain information
    219  * maxrem      ... the number of remailers in remailer[] and badchains[]
    220  * redirect_to ... if this is not-null it needs to be an email address.
    221  *                 in this case packet needs to be not only the body, but a
    222  *                 complete mixmaster packet of 20480 bytes in size (20 headers + body).
    223  *                 the chain given is prepended to the one already encrypted in
    224  *                 the existing message.  If this exceeds the allowed 20 hops in total
    225  *                 the message is corrupted, the last node will realize this.
    226  *                 This is useful if you want to reroute an existing mixmaster message
    227  *                 that has foo as the next hop via a chain so that the packet will
    228  *                 actually flow hop1,hop2,hop3,foo,....
    229  * ignore_constraints_if_necessary .. to be used when randhopping messages.
    230  *                 if a chain can not be constructed otherwhise, maxlat, minlat,
    231  *                 and minrel are ignored.
    232  * feedback    ... a buffer to write feedback to
    233  */
    234 {
    235   BUFFER *pid, *out, *header, *other, *encrypted, *key, *body;
    236   BUFFER *iv, *ivarray, *temp;
    237   BUFFER *pubkey;
    238   char addr[LINELEN];
    239   int thischain[20];
    240   int hop;
    241   int c, i;
    242   int timestamp = 0;
    243   int israndom = 0;
    244   int err = 1;
    245 
    246   body = buf_new();
    247   pid = buf_new();
    248   out = buf_new();
    249   header = buf_new();
    250   other = buf_new();
    251   key = buf_new();
    252   encrypted = buf_new();
    253   iv = buf_new();
    254   ivarray = buf_new();
    255   temp = buf_new();
    256 
    257   if (redirect_to != NULL) {
    258     assert(packet->length == 20480);
    259     buf_append(header, packet->data, 10240);
    260     buf_append(temp, packet->data + 10240, 10240);
    261     buf_clear(packet);
    262     buf_cat(packet, temp);
    263   } else 
    264     assert(packet->length == 10240);
    265 
    266   buf_setrnd(pid, 16);
    267 
    268   for (c = 0; c < numcopies; c++) {
    269     buf_set(body, packet);
    270 
    271     for (hop = 0; hop < chainlen; hop++)
    272       thischain[hop] = chain[hop];
    273 
    274     israndom = chain_rand(remailer, badchains, maxrem, thischain, chainlen, 0, ignore_constraints_if_necessary);
    275     if (israndom == -1) {
    276       err = -1;
    277       clienterr(feedback, "No reliable remailers!");
    278     }
    279     if ((numcopies > 1 || numpackets > 1) && !israndom && (chainlen != 1)) {
    280       clienterr(feedback,
    281 		"Multi-packet message without random remailers!");
    282       err = -1;
    283       goto end;
    284     }
    285     for (hop = 0; hop < chainlen; hop++) {
    286       switch (remailer[thischain[hop]].version) {
    287       case 2:
    288       case 3:			/* not implemented yet; fall back to version 2 */
    289 	/* create header chart to be encrypted with the session key */
    290 	if (numcopies > 1 && hop == 0 && redirect_to == NULL)
    291 	  buf_set(encrypted, pid);	/* same ID only at final hop */
    292 	else
    293 	  buf_setrnd(encrypted, 16);
    294 	buf_setrnd(key, 24);	/* key for encrypting the body */
    295 	buf_cat(encrypted, key);
    296 	buf_setrnd(iv, 8);	/* IV for encrypting the body */
    297 
    298 	if (hop > 0 || redirect_to != NULL) {
    299 	  /* IVs for header chart encryption */
    300 	  buf_setrnd(ivarray, 18 * 8);
    301 	  buf_cat(ivarray, iv);	/* 19th IV equals the body IV */
    302 
    303 	  buf_appendc(encrypted, 0);
    304 	  buf_cat(encrypted, ivarray);
    305 	  memset(addr, 0, 80);
    306 	  if (hop == 0) {
    307 	    assert(redirect_to != NULL);
    308 	    strncpy(addr, redirect_to, 80);
    309 	  } else {
    310 	    assert(hop > 0);
    311 	    strcpy(addr, remailer[thischain[hop - 1]].addr);
    312 	  };
    313 	  buf_append(encrypted, addr, 80);
    314 	} else {
    315 	  if (numpackets == 1)
    316 	    buf_appendc(encrypted, 1);
    317 	  else {
    318 	    buf_appendc(encrypted, 2);
    319 	    buf_appendc(encrypted, (byte) packetnum);
    320 	    buf_appendc(encrypted, (byte) numpackets);
    321 	  }
    322 	  buf_cat(encrypted, mid);
    323 	  buf_cat(encrypted, iv);	/* body encryption IV */
    324 	}
    325 
    326 	/* timestamp */
    327 	buf_appends(encrypted, "0000");
    328 	buf_appendc(encrypted, '\0');	/* timestamp magic */
    329 	timestamp = time(NULL) / SECONDSPERDAY - rnd_number(4);
    330 	buf_appendi_lo(encrypted, timestamp);
    331 
    332 	/* message digest for this header */
    333 	digest_md5(encrypted, temp);
    334 	buf_cat(encrypted, temp);
    335 	buf_pad(encrypted, 328);
    336 
    337 	/* encrypt message body */
    338 	buf_crypt(body, key, iv, ENCRYPT);
    339 
    340 	if (hop > 0 || redirect_to != NULL) {
    341 	  /* encrypt the other header charts */
    342 	  buf_clear(other);
    343 	  for (i = 0; i < 19; i++) {
    344 	    buf_clear(iv);
    345 	    buf_clear(temp);
    346 	    buf_append(iv, ivarray->data + 8 * i, 8);
    347 	    buf_append(temp, header->data + 512 * i, 512);
    348 	    buf_crypt(temp, key, iv, ENCRYPT);
    349 	    buf_cat(other, temp);
    350 	  }
    351 	} else
    352 	  buf_setrnd(other, 19 * 512);	/* fill with random data */
    353 
    354 	/* create session key and IV to encrypt the header ... */
    355 	buf_setrnd(key, 24);
    356 	buf_setrnd(iv, 8);
    357 	buf_crypt(encrypted, key, iv, ENCRYPT);
    358 	pubkey = buf_new();
    359 	err = db_getpubkey(remailer[thischain[hop]].keyid, pubkey);
    360 	if (err == -1)
    361 	  goto end;
    362 	err = pk_encrypt(key, pubkey);	/* ... and encrypt the
    363 					   session key */
    364 	buf_free(pubkey);
    365 	if (err == -1 || key->length != 128) {
    366 	  clienterr(feedback, "Encryption failed!");
    367 	  err = -1;
    368 	  goto end;
    369 	}
    370 	/* now build the new header */
    371 	buf_clear(header);
    372 	buf_append(header, remailer[thischain[hop]].keyid, 16);
    373 	buf_appendc(header, 128);
    374 	buf_cat(header, key);
    375 	buf_cat(header, iv);
    376 	buf_cat(header, encrypted);
    377 	buf_pad(header, 512);
    378 	buf_cat(header, other);
    379 	break;
    380       default:
    381 	err = -1;
    382 	goto end;
    383       }
    384     }
    385 
    386     /* build the message */
    387     buf_sets(out, remailer[thischain[chainlen - 1]].addr);
    388     buf_nl(out);
    389     buf_cat(out, header);
    390     buf_cat(out, body);
    391     assert(header->length == 10240 && body->length == 10240);
    392     mix_pool(out, INTERMEDIATE, -1);
    393 
    394     if (feedback) {
    395       for (hop = chainlen - 1; hop >= 0; hop--) {
    396 	buf_appends(feedback, remailer[thischain[hop]].name);
    397 	if (hop > 0)
    398 	  buf_appendc(feedback, ',');
    399       }
    400       buf_nl(feedback);
    401     }
    402   }
    403 end:
    404   buf_free(pid);
    405   buf_free(body);
    406   buf_free(out);
    407   buf_free(header);
    408   buf_free(temp);
    409   buf_free(other);
    410   buf_free(key);
    411   buf_free(encrypted);
    412   buf_free(iv);
    413   buf_free(ivarray);
    414   return (err);
    415 }
    416 
    417 int redirect_message(BUFFER *sendmsg, char *chainstr, int numcopies, BUFFER *feedback)
    418 {
    419   BUFFER *field;
    420   BUFFER *content;
    421   BUFFER *line;
    422   char recipient[80] = "";
    423   int num = 0;
    424   int err = 0;
    425   int c;
    426   int hop;
    427 
    428   REMAILER remailer[MAXREM];
    429   int chain[20];
    430   int thischain[20];
    431   int chainlen;
    432   int badchains[MAXREM][MAXREM];
    433   int maxrem;
    434   int tempchain[20];
    435   int tempchainlen;
    436   int israndom;
    437 
    438   field = buf_new();
    439   content = buf_new();
    440   line = buf_new();
    441 
    442   if (numcopies == 0)
    443     numcopies = NUMCOPIES;
    444   if (numcopies > 10)
    445     numcopies = 10;
    446 
    447   /* Find the recipient */
    448   while (buf_getheader(sendmsg, field, content) == 0)
    449     if (bufieq(field, "to")) {
    450       strncpy(recipient, content->data, sizeof(recipient));
    451       num++;
    452     };
    453   if (num != 1) {
    454     clienterr(feedback, "Did not find exactly one To: address!");
    455     err = -1;
    456     goto end;
    457   };
    458 
    459   /* Dearmor the message */
    460   err = mix_dearmor(sendmsg, sendmsg);
    461   if (err == -1)
    462     goto end;
    463   assert (sendmsg->length == 20480);
    464 
    465   /* Check the chain */
    466   maxrem = mix2_rlist(remailer, badchains);
    467   if (maxrem < 1) {
    468     clienterr(feedback, "No remailer list!");
    469     err = -1;
    470     goto end;
    471   }
    472   chainlen = chain_select(chain, chainstr, maxrem, remailer, 0, line);
    473   if (chainlen < 1) {
    474     if (line->length)
    475       clienterr(feedback, line->data);
    476     else
    477       clienterr(feedback, "Invalid remailer chain!");
    478     err = -1;
    479     goto end;
    480   } else if (chainlen >= 20) {
    481     clienterr(feedback, "A chainlength of 20 will certainly destroy the message!");
    482     err = -1;
    483     goto end;
    484   };
    485 
    486 
    487   for (c = 0; c < numcopies; c++) {
    488     /* if our recipient is a remailer we want to make sure we're not using a known broken chain.
    489      * therefore we need to pick the final remailer with care */
    490     for (hop = 0; hop < chainlen; hop++)
    491       thischain[hop] = chain[hop];
    492     if (thischain[0] == 0) {
    493       /* Find out, if recipient is a remailer */
    494       tempchainlen = chain_select(tempchain, recipient, maxrem, remailer, 0, line);
    495       if (tempchainlen < 1 && line->length == 0) {
    496 	/* recipient is apparently not a remailer we know about */
    497 	;
    498       } else {
    499 	/* Build a new chain, based on the one we already selected but
    500 	 * with the recipient as the final hop.
    501 	 * This is so that chain_rand properly selects nodes based on
    502 	 * broken chains and DISTANCE */
    503 	assert(chainlen < 20);
    504 	for (hop = 0; hop < chainlen; hop++)
    505 	  thischain[hop+1] = thischain[hop];
    506 	thischain[0] = tempchain[0];
    507 
    508 	israndom = chain_rand(remailer, badchains, maxrem, thischain, chainlen + 1, 0, 0);
    509 	if (israndom == -1) {
    510 	  err = -1;
    511 	  clienterr(feedback, "No reliable remailers!");
    512 	  goto end;
    513 	}
    514 
    515 	/* Remove the added recipient hop */
    516 	for (hop = 0; hop < chainlen; hop++)
    517 	  thischain[hop] = thischain[hop + 1];
    518       };
    519     };
    520 
    521     /* queue the packet */
    522     if (send_packet(1, sendmsg, thischain, chainlen,
    523 	    -1, -1, NULL,
    524 	    remailer, badchains, maxrem, recipient, 0, feedback) == -1)
    525       err = -1;
    526   };
    527 
    528 end:
    529   buf_free(field);
    530   buf_free(content);
    531   buf_free(line);
    532   return (err);
    533 }
    534 
    535 int mix2_encrypt(int type, BUFFER *message, char *chainstr, int numcopies,
    536 		 int ignore_constraints_if_necessary,
    537 		 BUFFER *feedback)
    538 {
    539   /* returns 0 on success, -1 on error. feedback contains the selected
    540      remailer chain or an error message
    541 
    542    ignore_constraints_if_necessary .. to be used when randhopping messages.
    543                                    if a chain can not be constructed otherwhise,
    544                                    maxlat, minlat, and minrel are ignored.
    545      */
    546 
    547   REMAILER remailer[MAXREM];
    548   int badchains[MAXREM][MAXREM];
    549   int maxrem;
    550   BUFFER *line, *field, *content, *header, *msgdest, *msgheader, *body,
    551       *temp, *mid;
    552   byte numdest = 0, numhdr = 0;
    553   char hdrline[LINELEN];
    554   BUFFER *packet;
    555   int chain[20];
    556   int chainlen;
    557   int i;
    558   int err = 0;
    559 
    560   mix_init(NULL);
    561   packet = buf_new();
    562   line = buf_new();
    563   field = buf_new();
    564   content = buf_new();
    565   msgheader = buf_new();
    566   msgdest = buf_new();
    567   body = buf_new();
    568   temp = buf_new();
    569   mid = buf_new();
    570   header = buf_new();
    571   if (feedback)
    572     buf_reset(feedback);
    573 
    574   if (numcopies == 0)
    575     numcopies = NUMCOPIES;
    576   if (numcopies > 10)
    577     numcopies = 10;
    578 
    579   maxrem = mix2_rlist(remailer, badchains);
    580   if (maxrem < 1) {
    581     clienterr(feedback, "No remailer list!");
    582     err = -1;
    583     goto end;
    584   }
    585   chainlen = chain_select(chain, chainstr, maxrem, remailer, 0, line);
    586   if (chainlen < 1) {
    587     if (line->length)
    588       clienterr(feedback, line->data);
    589     else
    590       clienterr(feedback, "Invalid remailer chain!");
    591     err = -1;
    592     goto end;
    593   }
    594   if (chain[0] == 0)
    595     chain[0] = chain_randfinal(type, remailer, badchains, maxrem, 0, chain, chainlen, ignore_constraints_if_necessary);
    596 
    597   if (chain[0] == -1) {
    598     clienterr(feedback, "No reliable remailers!");
    599     err = -1;
    600     goto end;
    601   }
    602   switch (remailer[chain[0]].version) {
    603   case 2:
    604     if (type == MSG_NULL) {
    605       memset(hdrline, 0, 80);
    606       strcpy(hdrline, "null:");
    607       buf_append(msgdest, hdrline, 80);
    608       numdest++;
    609     } else
    610       while (buf_getheader(message, field, content) == 0) {
    611 	if (bufieq(field, "to")) {
    612 	  memset(hdrline, 0, 80);
    613 	  strncpy(hdrline, content->data, 80);
    614 	  buf_append(msgdest, hdrline, 80);
    615 	  numdest++;
    616 	} else if (type == MSG_POST && bufieq(field, "newsgroups")) {
    617 	  memset(hdrline, 0, 80);
    618 	  strcpy(hdrline, "post: ");
    619 	  strcatn(hdrline, content->data, 80);
    620 	  buf_append(msgdest, hdrline, 80);
    621 	  numdest++;
    622 	} else {
    623 	  buf_clear(header);
    624 	  buf_appendheader(header, field, content);
    625 	  hdr_encode(header, 80);
    626 	  while (buf_getline(header, line) == 0) {
    627 	    /* paste in encoded header entry */
    628 	    memset(hdrline, 0, 80);
    629 	    strncpy(hdrline, line->data, 80);
    630 	    buf_append(msgheader, hdrline, 80);
    631 	    numhdr++;
    632 	  }
    633 	}
    634       }
    635     buf_appendc(body, numdest);
    636     buf_cat(body, msgdest);
    637     buf_appendc(body, numhdr);
    638     buf_cat(body, msgheader);
    639 
    640     if (type != MSG_NULL) {
    641       buf_rest(temp, message);
    642       if (temp->length > 10236 && remailer[chain[0]].flags.compress)
    643 	buf_compress(temp);
    644       buf_cat(body, temp);
    645       buf_reset(temp);
    646     }
    647     buf_setrnd(mid, 16);	/* message ID */
    648     for (i = 0; i <= body->length / 10236; i++) {
    649       long length;
    650 
    651       length = body->length - i * 10236;
    652       if (length > 10236)
    653 	length = 10236;
    654       buf_clear(packet);
    655       buf_appendl_lo(packet, length);
    656       buf_append(packet, body->data + i * 10236, length);
    657       buf_pad(packet, 10240);
    658       if (send_packet(numcopies, packet, chain, chainlen,
    659 		      i + 1, body->length / 10236 + 1,
    660 		      mid, remailer, badchains, maxrem, NULL, ignore_constraints_if_necessary, feedback) == -1)
    661 	err = -1;
    662     }
    663     break;
    664   case 3:
    665     NOT_IMPLEMENTED;
    666     break;
    667   default:
    668     fprintf(stderr, "%d\n", chain[0]);
    669     clienterr(feedback, "Unknown remailer version!");
    670     err = -1;
    671   }
    672 
    673 end:
    674   buf_free(packet);
    675   buf_free(line);
    676   buf_free(field);
    677   buf_free(content);
    678   buf_free(header);
    679   buf_free(msgheader);
    680   buf_free(msgdest);
    681   buf_free(body);
    682   buf_free(temp);
    683   buf_free(mid);
    684   return (err);
    685 }