Remove support for looking at old directory/routerdesc elements; mark non-new elements optional; switch to new format for dir-signing-key; start accepting newer elements so we can mark them as non-opt later; make tor-spec say the right stuff.

svn:r4154
This commit is contained in:
Nick Mathewson 2005-05-02 21:22:31 +00:00
parent be4a496527
commit 69dc4e1675
8 changed files with 82 additions and 152 deletions

View File

@ -188,12 +188,6 @@ TODO: (very soon)
onion router in the circuit; the public key hash is the SHA1 hash of the
PKCS#1 ASN1 encoding of the next onion router's identity (signing) key.
[XXXX Before 0.0.8, EXTEND cells did not include the public key hash.
Servers running 0.0.8 distinguish the old-style cells based on the
length of payloads. (Servers running 0.0.7 blindly pass on the extend
cell regardless of length.) In a future release, old-style EXTEND
cells will not be supported.]
The payload for a CREATED cell, or the relay payload for an
EXTENDED cell, contains:
DH data (g^y) [128 bytes]
@ -662,8 +656,6 @@ The items' formats are as follows:
over any ten second period in the past day, and another sustained
input. The "observed" value is the lesser of these two numbers.
[bandwidth-observed was not present before 0.0.8.]
"platform" string
A human-readable string describing the system on which this OR is
@ -680,6 +672,17 @@ The items' formats are as follows:
in hex, with spaces after every 4 characters) for this router's
identity key.
[We didn't start parsing this line until Tor 0.1.0.6-rc; it should
be marked with "opt" until earlier versions of Tor are obsolete.]
"hibernating" 0|1
If the value is 1, then the Tor server was hibernating when the
descriptor was published, and shouldn't be used to build circuits.
[We didn't start parsing this line until Tor 0.1.0.6-rc; it should
be marked with "opt" until earlier versions of Tor are obsolete.]
"uptime"
The number of seconds that this OR process has been running.
@ -709,17 +712,6 @@ The items' formats are as follows:
The router descriptor is invalid unless the signature is performed
with the router's identity key.
"dircacheport" port NL
Same as declaring "port" as this OR's directory port in the 'router'
line. At most one of dircacheport and the directory port in the router
line may be non-zero.
[Obsolete; will go away once 0.0.8 is dead. Older versions of Tor
did poorly when non-authoritative directories had a non-zero directory
port. To transition, Tor 0.0.8 used dircacheport for
nonauthoritative directories.]
"contact" info NL
Describes a way to contact the server's administrator, preferably
@ -743,6 +735,9 @@ The items' formats are as follows:
the end of the most recent interval. The numbers are the number of
bytes used in the most recent intervals, ordered from oldest to newest.
[We didn't start parsing these lines until Tor 0.1.0.6-rc; they should
be marked with "opt" until earlier versions of Tor are obsolete.]
nickname ::= between 1 and 19 alphanumeric characters, case-insensitive.
exitpattern ::= addrspec ":" portspec
@ -764,7 +759,7 @@ line, they must appear in the "ports" lines.
A Directory begins with a "signed-directory" item, followed by one each of
the following, in any order: "recommended-software", "published",
"router-status", "directory-signing-key". It may include any number of "opt"
"router-status", "dir-signing-key". It may include any number of "opt"
items. After these items, a directory includes any number of router
descriptors, and a single "directory-signature" item.
@ -776,7 +771,7 @@ descriptors, and a single "directory-signature" item.
The time at which this directory was generated and signed, in GMT.
"directory-signing-key"
"dir-signing-key"
The key used to sign this directory; see "signing-key" for format.
@ -816,8 +811,6 @@ descriptors, and a single "directory-signature" item.
When parsing this line, clients should only mark a router as
'verified' if its nickname AND digest match the one provided.
[XXXX 'router-status' was added in 0.0.9pre5; older directory code
uses 'running-routers' instead.]
"directory-signature" nickname-of-dirserver NL Signature

View File

@ -630,7 +630,7 @@ handle_getinfo_helper(const char *question, char **answer)
routerlist_t *routerlist;
router_get_routerlist(&routerlist);
if (!routerlist || !routerlist->routers ||
list_server_status(routerlist->routers, NULL, answer) < 0) {
list_server_status(routerlist->routers, answer) < 0) {
return -1;
}
} else if (!strcmpstart(question, "addr-mappings/")) {

View File

@ -490,13 +490,11 @@ dirserv_load_from_directory_string(const char *dir)
/**
* Allocate and return a description of the status of the server <b>desc</b>,
* for use in a running-routers line (if <b>rr_format</b> is true), or in a
* router-status line (if <b>rr_format</b> is false. The server is listed
* for use in a router-status line. The server is listed
* as running iff <b>is_live</b> is true.
*/
static char *
list_single_server_status(routerinfo_t *desc, int is_live,
int rr_format)
list_single_server_status(routerinfo_t *desc, int is_live)
{
char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */
char *cp;
@ -510,29 +508,21 @@ list_single_server_status(routerinfo_t *desc, int is_live,
if (desc->is_verified) {
strlcpy(cp, desc->nickname, sizeof(buf)-(cp-buf));
cp += strlen(cp);
if (!rr_format)
*cp++ = '=';
}
if (!desc->is_verified || !rr_format) {
*cp++ = '$';
base16_encode(cp, HEX_DIGEST_LEN+1, desc->identity_digest,
DIGEST_LEN);
*cp++ = '=';
}
*cp++ = '$';
base16_encode(cp, HEX_DIGEST_LEN+1, desc->identity_digest,
DIGEST_LEN);
return tor_strdup(buf);
}
/** Based on the routerinfo_ts in <b>routers</b>, allocate the
* contents of a running-routers line and a router-status line, and
* store them in *<b>running_routers_out</b> and
* *<b>router_status_out</b> respectively. If either is NULL, skip
* it. Return 0 on success, -1 on failure.
* contents of a router-status line, and store it in
* *<b>router_status_out</b>. Return 0 on success, -1 on failure.
*/
int
list_server_status(smartlist_t *routers, char **running_routers_out, char **router_status_out)
list_server_status(smartlist_t *routers, char **router_status_out)
{
/* List of entries in running-routers style: An optional !, then either
* a nickname or a dollar-prefixed hexdigest. */
smartlist_t *rr_entries;
/* List of entries in a router-status style: An optional !, then an optional
* equals-suffixed nickname, then a dollar-prefixed hexdigest. */
smartlist_t *rs_entries;
@ -542,9 +532,8 @@ list_server_status(smartlist_t *routers, char **running_routers_out, char **rout
* series.
*/
int authdir_mode = get_options()->AuthoritativeDir;
tor_assert(running_routers_out || router_status_out);
tor_assert(router_status_out);
rr_entries = smartlist_create();
rs_entries = smartlist_create();
SMARTLIST_FOREACH(routers, routerinfo_t *, ri,
@ -562,18 +551,12 @@ list_server_status(smartlist_t *routers, char **running_routers_out, char **rout
} else {
is_live = ri->is_running;
}
smartlist_add(rr_entries, list_single_server_status(ri, is_live, 1));
smartlist_add(rs_entries, list_single_server_status(ri, is_live, 0));
smartlist_add(rs_entries, list_single_server_status(ri, is_live));
});
if (running_routers_out)
*running_routers_out = smartlist_join_strings(rr_entries, " ", 0,NULL);
if (router_status_out)
*router_status_out = smartlist_join_strings(rs_entries, " ", 0,NULL);
*router_status_out = smartlist_join_strings(rs_entries, " ", 0,NULL);
SMARTLIST_FOREACH(rr_entries, char *, cp, tor_free(cp));
SMARTLIST_FOREACH(rs_entries, char *, cp, tor_free(cp));
smartlist_free(rr_entries);
smartlist_free(rs_entries);
return 0;
@ -613,7 +596,7 @@ dirserv_dump_directory_to_string(char **dir_out,
crypto_pk_env_t *private_key)
{
char *cp;
char *running_routers, *router_status;
char *router_status;
char *identity_pkey; /* Identity key, DER64-encoded. */
char *recommended_versions;
char digest[20];
@ -623,6 +606,7 @@ dirserv_dump_directory_to_string(char **dir_out,
char *buf = NULL;
size_t buf_len;
int i;
size_t identity_pkey_len;
tor_assert(dir_out);
*dir_out = NULL;
@ -630,27 +614,14 @@ dirserv_dump_directory_to_string(char **dir_out,
if (!descriptor_list)
descriptor_list = smartlist_create();
if (list_server_status(descriptor_list, &running_routers, &router_status))
if (list_server_status(descriptor_list, &router_status))
return -1;
/* ASN.1-encode the public key. This is a temporary measure; once
* everyone is running 0.0.9pre3 or later, we can shift to using a
* PEM-encoded key instead.
*/
#if 1
if (crypto_pk_DER64_encode_public_key(private_key, &identity_pkey)<0) {
if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey,
&identity_pkey_len)<0) {
log_fn(LOG_WARN,"write identity_pkey to string failed!");
return -1;
}
#else
{
int l;
if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey,&l)<0) {
log_fn(LOG_WARN,"write identity_pkey to string failed!");
return -1;
}
}
#endif
{
smartlist_t *versions;
@ -669,7 +640,7 @@ dirserv_dump_directory_to_string(char **dir_out,
published_on = time(NULL);
format_iso_time(published, published_on);
buf_len = 2048+strlen(recommended_versions)+strlen(running_routers)+
buf_len = 2048+strlen(recommended_versions)+
strlen(router_status);
SMARTLIST_FOREACH(descriptor_list, routerinfo_t *, ri,
buf_len += strlen(ri->signed_descriptor));
@ -683,14 +654,12 @@ dirserv_dump_directory_to_string(char **dir_out,
"signed-directory\n"
"published %s\n"
"recommended-software %s\n"
"running-routers %s\n"
"opt router-status %s\n"
"opt dir-signing-key %s\n\n",
published, recommended_versions, running_routers, router_status,
"router-status %s\n"
"dir-signing-key\n%s\n",
published, recommended_versions, router_status,
identity_pkey);
tor_free(recommended_versions);
tor_free(running_routers);
tor_free(router_status);
tor_free(identity_pkey);
i = strlen(buf);
@ -890,31 +859,19 @@ static int generate_runningrouters(crypto_pk_env_t *private_key)
size_t len;
time_t published_on;
char *identity_pkey; /* Identity key, DER64-encoded. */
size_t identity_pkey_len;
if (!descriptor_list)
descriptor_list = smartlist_create();
if (list_server_status(descriptor_list, NULL, &router_status)) {
if (list_server_status(descriptor_list, &router_status)) {
goto err;
}
/* ASN.1-encode the public key. This is a temporary measure; once
* everyone is running 0.0.9pre3 or later, we can shift to using a
* PEM-encoded key instead.
*/
#if 1
if (crypto_pk_DER64_encode_public_key(private_key, &identity_pkey)<0) {
if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey,
&identity_pkey_len)<0) {
log_fn(LOG_WARN,"write identity_pkey to string failed!");
goto err;
}
#else
{
int l;
if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey,&l)<0) {
log_fn(LOG_WARN,"write identity_pkey to string failed!");
goto err;
}
}
#endif
published_on = time(NULL);
format_iso_time(published, published_on);
@ -923,7 +880,7 @@ static int generate_runningrouters(crypto_pk_env_t *private_key)
tor_snprintf(s, len, "network-status\n"
"published %s\n"
"router-status %s\n"
"opt dir-signing-key %s\n"
"dir-signing-key\n%s"
"directory-signature %s\n"
"-----BEGIN SIGNATURE-----\n",
published, router_status, identity_pkey, get_options()->Nickname);

View File

@ -699,6 +699,8 @@ typedef struct {
smartlist_t *declared_family; /**< Nicknames of router which this router
* claims are its family. */
char *contact_info; /**< Declared contact info for this router. */
} routerinfo_t;
/** Contents of a running-routers list */
@ -706,7 +708,6 @@ typedef struct running_routers_t {
time_t published_on; /**< When was the list marked as published? */
/** Which ORs are on the list? Entries may be prefixed with ! and $. */
smartlist_t *running_routers;
int is_running_routers_format; /**< Are we using the old entry format? */
} running_routers_t;
/** Contents of a directory of onion routers. */
@ -1472,8 +1473,7 @@ const char *dirserv_get_nickname_by_digest(const char *digest);
int dirserv_add_descriptor(const char **desc, const char **msg);
int dirserv_load_from_directory_string(const char *dir);
void dirserv_free_descriptors(void);
int list_server_status(smartlist_t *routers,
char **running_routers_out, char **router_status_out);
int list_server_status(smartlist_t *routers, char **router_status_out);
void dirserv_remove_old_servers(int age);
int dirserv_dump_directory_to_string(char **dir_out,
crypto_pk_env_t *private_key);
@ -1811,12 +1811,10 @@ void routerlist_update_from_runningrouters(routerlist_t *list,
running_routers_t *rr);
int routers_update_status_from_entry(smartlist_t *routers,
time_t list_time,
const char *s,
int rr_format);
const char *s);
int router_update_status_from_smartlist(routerinfo_t *r,
time_t list_time,
smartlist_t *running_list,
int rr_format);
smartlist_t *running_list);
void add_trusted_dir_server(const char *addr, uint16_t port,const char *digest);
void clear_trusted_dir_servers(void);

View File

@ -814,9 +814,9 @@ int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
if (router->declared_family && smartlist_len(router->declared_family)) {
size_t n;
char *s = smartlist_join_strings(router->declared_family, " ", 0, &n);
n += strlen("opt family ") + 2; /* 1 for \n, 1 for \0. */
n += strlen("family ") + 2; /* 1 for \n, 1 for \0. */
family_line = tor_malloc(n);
tor_snprintf(family_line, n, "opt family %s\n", s);
tor_snprintf(family_line, n, "family %s\n", s);
tor_free(s);
} else {
family_line = tor_strdup("");
@ -828,7 +828,7 @@ int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
"platform %s\n"
"published %s\n"
"opt fingerprint %s\n"
"opt uptime %ld\n"
"uptime %ld\n"
"bandwidth %d %d %d\n"
"onion-key\n%s"
"signing-key\n%s%s%s%s",
@ -857,7 +857,7 @@ int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
written = result;
if (get_options()->ContactInfo && strlen(get_options()->ContactInfo)) {
result = tor_snprintf(s+written,maxlen-written, "opt contact %s\n",
result = tor_snprintf(s+written,maxlen-written, "contact %s\n",
get_options()->ContactInfo);
if (result<0)
return -1;

View File

@ -916,8 +916,7 @@ router_load_single_router(const char *s, const char **msg)
running_routers_t *rr = routerlist->running_routers;
router_update_status_from_smartlist(ri,
rr->published_on,
rr->running_routers,
rr->is_running_routers_format);
rr->running_routers);
}
if (router_add_to_routerlist(ri, msg)<0) {
log_fn(LOG_WARN, "Couldn't add router to list; dropping.");
@ -1276,7 +1275,7 @@ void routerlist_update_from_runningrouters(routerlist_t *list,
smartlist_add_all(all_routers,list->routers);
SMARTLIST_FOREACH(rr->running_routers, const char *, cp,
routers_update_status_from_entry(all_routers, rr->published_on,
cp, rr->is_running_routers_format));
cp));
smartlist_free(all_routers);
list->running_routers_updated_on = rr->published_on;
}
@ -1302,8 +1301,7 @@ void routerlist_update_from_runningrouters(routerlist_t *list,
*/
int routers_update_status_from_entry(smartlist_t *routers,
time_t list_time,
const char *s,
int rr_format)
const char *s)
{
int is_running = 1;
int is_verified = 0;
@ -1365,16 +1363,9 @@ int routers_update_status_from_entry(smartlist_t *routers,
}
/* Make sure that the entry was in the right format. */
if (rr_format) {
if (is_verified == hex_digest_set) {
log_fn(LOG_WARN, "Invalid syntax for running-routers member (%s)", s);
return -1;
}
} else {
if (!hex_digest_set) {
log_fn(LOG_WARN, "Invalid syntax for router-status member (%s)", s);
return -1;
}
if (!hex_digest_set) {
log_fn(LOG_WARN, "Invalid syntax for router-status member (%s)", s);
return -1;
}
/* Okay, we're done parsing. For all routers that match, update their status.
@ -1383,11 +1374,11 @@ int routers_update_status_from_entry(smartlist_t *routers,
{
int nickname_matches = is_verified && !strcasecmp(r->nickname, nickname);
int digest_matches = !memcmp(digest, r->identity_digest, DIGEST_LEN);
if (nickname_matches && (digest_matches||rr_format))
if (nickname_matches && digest_matches)
r->is_verified = 1;
else if (digest_matches)
r->is_verified = 0;
if (digest_matches || (nickname_matches&&rr_format))
if (digest_matches)
if (r->status_set_at < list_time) {
r->is_running = is_running;
r->status_set_at = time(NULL);
@ -1402,14 +1393,13 @@ int routers_update_status_from_entry(smartlist_t *routers,
int
router_update_status_from_smartlist(routerinfo_t *router,
time_t list_time,
smartlist_t *running_list,
int rr_format)
smartlist_t *running_list)
{
smartlist_t *rl;
rl = smartlist_create();
smartlist_add(rl,router);
SMARTLIST_FOREACH(running_list, const char *, cp,
routers_update_status_from_entry(rl,list_time,cp,rr_format));
routers_update_status_from_entry(rl,list_time,cp));
smartlist_free(rl);
return 0;
}

View File

@ -38,12 +38,15 @@ typedef enum {
K_OPT,
K_BANDWIDTH,
K_PORTS,
K_DIRCACHEPORT,
K_CONTACT,
K_NETWORK_STATUS,
K_UPTIME,
K_DIR_SIGNING_KEY,
K_FAMILY,
K_FINGERPRINT,
K_HIBERNATING,
K_READ_HISTORY,
K_WRITE_HISTORY,
_UNRECOGNIZED,
_ERR,
_EOF,
@ -114,12 +117,15 @@ static struct {
{ "platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ, RTR_ONLY },
{ "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ, ANY },
{ "opt", K_OPT, CONCAT_ARGS, OBJ_OK, ANY },
{ "dircacheport", K_DIRCACHEPORT, ARGS, NO_OBJ, RTR_ONLY },
{ "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ, ANY },
{ "network-status", K_NETWORK_STATUS, NO_ARGS, NO_OBJ, DIR_ONLY },
{ "uptime", K_UPTIME, ARGS, NO_OBJ, RTR_ONLY },
{ "dir-signing-key", K_DIR_SIGNING_KEY, ARGS, OBJ_OK, DIR_ONLY },
{ "family", K_FAMILY, ARGS, NO_OBJ, RTR_ONLY },
{ "fingerprint", K_FINGERPRINT, ARGS, NO_OBJ, ANY },
{ "hibernating", K_HIBERNATING, ARGS, NO_OBJ, RTR_ONLY },
{ "read-history", K_READ_HISTORY, ARGS, NO_OBJ, RTR_ONLY },
{ "write-history", K_WRITE_HISTORY, ARGS, NO_OBJ, RTR_ONLY },
{ NULL, -1, NO_ARGS, NO_OBJ, ANY }
};
@ -358,7 +364,6 @@ router_parse_routerlist_from_directory(const char *str,
char digest[DIGEST_LEN];
routerlist_t *new_dir = NULL;
char *versions = NULL;
int nickname_list_is_running_routers;
smartlist_t *good_nickname_list = NULL;
time_t published_on;
int i, r;
@ -466,14 +471,11 @@ router_parse_routerlist_from_directory(const char *str,
/* Prefer router-status, then running-routers. */
if (!(tok = find_first_by_keyword(tokens, K_ROUTER_STATUS))) {
if (!(tok = find_first_by_keyword(tokens, K_RUNNING_ROUTERS))) {
log_fn(LOG_WARN,
"Missing running-routers/router-status line from directory.");
goto err;
}
log_fn(LOG_WARN,
"Missing router-status line from directory.");
goto err;
}
nickname_list_is_running_routers = (tok->tp == K_RUNNING_ROUTERS);
good_nickname_list = smartlist_create();
for (i=0; i<tok->n_args; ++i) {
smartlist_add(good_nickname_list, tok->args[i]);
@ -497,7 +499,7 @@ router_parse_routerlist_from_directory(const char *str,
routerinfo_t *me = router_get_my_routerinfo();
if (me) {
if (router_update_status_from_smartlist(me,
published_on, good_nickname_list, tok->tp==K_RUNNING_ROUTERS)==1 &&
published_on, good_nickname_list)==1 &&
me->is_verified == 0 && !have_warned_about_unverified_status) {
log_fn(LOG_WARN,"Dirserver '%s' lists your server as unverified. Please consider sending your identity fingerprint to the tor-ops.", dirnickname);
have_warned_about_unverified_status = 1;
@ -510,8 +512,6 @@ router_parse_routerlist_from_directory(const char *str,
new_dir->running_routers = tor_malloc_zero(sizeof(running_routers_t));
new_dir->running_routers->published_on = published_on;
new_dir->running_routers->running_routers = good_nickname_list;
new_dir->running_routers->is_running_routers_format =
nickname_list_is_running_routers;
SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok));
smartlist_free(tokens);
@ -599,7 +599,6 @@ router_parse_runningrouters(const char *str, int write_to_cache)
new_list = tor_malloc_zero(sizeof(running_routers_t));
new_list->published_on = published_on;
new_list->running_routers = smartlist_create();
new_list->is_running_routers_format = (tok->tp == K_RUNNING_ROUTERS);
for (i=0;i<tok->n_args;++i) {
smartlist_add(new_list->running_routers, tok->args[i]);
}
@ -659,6 +658,8 @@ static crypto_pk_env_t *find_dir_signing_key(const char *str)
key = tok->key;
tok->key = NULL; /* steal reference. */
} else if (tok->n_args >= 1) {
/** XXXX Once all the directories are running 0.1.0.6-rc or later, we
* can remove this logic. */
key = crypto_pk_DER64_decode_public_key(tok->args[0]);
if (!key) {
log_fn(LOG_WARN, "Unparseable dir-signing-key argument");
@ -801,8 +802,7 @@ router_parse_list_from_string(const char **s, routerlist_t **dest,
if (good_nickname_list) {
SMARTLIST_FOREACH(good_nickname_list, const char *, cp,
routers_update_status_from_entry(routers, published_on,
cp, rr_format));
routers_update_status_from_entry(routers, published_on, cp));
}
if (*dest)
@ -893,17 +893,6 @@ routerinfo_t *router_parse_entry_from_string(const char *s,
ports_set = 1;
}
tok = find_first_by_keyword(tokens, K_DIRCACHEPORT);
if (tok) {
if (router->dir_port)
log_fn(LOG_WARN,"Redundant dircacheport line");
if (tok->n_args != 1) {
log_fn(LOG_WARN,"Wrong # of arguments to \"dircacheport\"");
goto err;
}
router->dir_port = (uint16_t) tor_parse_long(tok->args[0],10,1,65535,NULL,NULL);
}
tok = find_first_by_keyword(tokens, K_BANDWIDTH);
if (tok && bw_set) {
log_fn(LOG_WARN,"Redundant bandwidth line");
@ -964,6 +953,10 @@ routerinfo_t *router_parse_entry_from_string(const char *s,
router->platform = tor_strdup(tok->args[0]);
}
if ((tok = find_first_by_keyword(tokens, K_CONTACT))) {
router->contact_info = tor_strdup(tok->args[0]);
}
exit_policy_tokens = find_all_exitpolicy(tokens);
SMARTLIST_FOREACH(exit_policy_tokens, directory_token_t *, t,
if (router_add_exit_policy(router,t)<0) {

View File

@ -1148,7 +1148,7 @@ test_dir_format(void)
"opt fingerprint ");
test_assert(!crypto_pk_get_fingerprint(pk2, fingerprint, 1));
strcat(buf2, fingerprint);
strcat(buf2, "\nopt uptime 0\n"
strcat(buf2, "\nuptime 0\n"
/* XXX the "0" above is hardcoded, but even if we made it reflect
* uptime, that still wouldn't make it right, because the two
* descriptors might be made on different seconds... hm. */
@ -1395,4 +1395,3 @@ main(int c, char**v) {
else
return 0;
}