Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ZerXes
Tor
Commits
be874358
Commit
be874358
authored
Oct 04, 2003
by
Roger Dingledine
Browse files
wrap strdup; prefer time() to gettimeofday()
svn:r538
parent
f563bbd2
Changes
15
Hide whitespace changes
Inline
Side-by-side
src/common/log.c
View file @
be874358
...
...
@@ -40,7 +40,7 @@ static INLINE void format_msg(char *buf, size_t buf_len,
buf_len
-=
2
;
/* subtract 2 characters so we have room for \n\0 */
my
_gettimeofday
(
&
now
);
tor
_gettimeofday
(
&
now
);
t
=
(
time_t
)
now
.
tv_sec
;
n
=
strftime
(
buf
,
buf_len
,
"%b %d %H:%M:%S"
,
localtime
(
&
t
));
...
...
src/common/util.c
View file @
be874358
...
...
@@ -9,7 +9,7 @@
#endif
/*
* Memory
* Memory
wrappers
*/
void
*
tor_malloc
(
size_t
size
)
{
...
...
@@ -22,17 +22,26 @@ void *tor_malloc(size_t size) {
exit
(
1
);
}
memset
(
result
,
'X'
,
size
);
/* XXX deadbeef to encourage bugs */
return
result
;
}
char
*
tor_strdup
(
const
char
*
s
)
{
char
*
dup
;
assert
(
s
);
dup
=
strdup
(
s
);
if
(
!
dup
)
{
log_fn
(
LOG_ERR
,
"Out of memory. Dying."
);
exit
(
1
);
}
return
dup
;
}
/*
* Time
*/
void
my_gettimeofday
(
struct
timeval
*
timeval
)
{
void
tor_gettimeofday
(
struct
timeval
*
timeval
)
{
#ifdef HAVE_GETTIMEOFDAY
if
(
gettimeofday
(
timeval
,
NULL
))
{
log_fn
(
LOG_ERR
,
"gettimeofday failed."
);
...
...
@@ -141,6 +150,10 @@ void set_socket_nonblocking(int socket)
* Process control
*/
/* Minimalist interface to run a void function in the background. On
* unix calls fork, on win32 calls beginthread. Returns -1 on failure.
* func should not return, but rather should call spawn_exit.
*/
int
spawn_func
(
int
(
*
func
)(
void
*
),
void
*
data
)
{
#ifdef MS_WINDOWS
...
...
@@ -294,6 +307,10 @@ int correct_socket_errno(int s)
/*
* Filesystem operations.
*/
/* Return FN_ERROR if filename can't be read, FN_NOENT if it doesn't
* exist, FN_FILE if it is a regular file, or FN_DIR if it's a
* directory. */
file_status_t
file_status
(
const
char
*
fname
)
{
struct
stat
st
;
...
...
@@ -311,6 +328,8 @@ file_status_t file_status(const char *fname)
return
FN_ERROR
;
}
/* Check whether dirname exists and is private. If yes returns
0. Else returns -1. */
int
check_private_dir
(
const
char
*
dirname
,
int
create
)
{
struct
stat
st
;
...
...
src/common/util.h
View file @
be874358
...
...
@@ -33,11 +33,9 @@
#endif
void
*
tor_malloc
(
size_t
size
);
char
*
tor_strdup
(
const
char
*
s
);
void
tor_gettimeofday
(
struct
timeval
*
timeval
);
/* Same as gettimeofday, but no need to check exit value. */
void
my_gettimeofday
(
struct
timeval
*
timeval
);
/* Returns the number of microseconds between start and end. Requires that
* end >= start, and that the number of microseconds < LONG_MAX. */
long
tv_udiff
(
struct
timeval
*
start
,
struct
timeval
*
end
);
void
tv_addms
(
struct
timeval
*
a
,
long
ms
);
...
...
@@ -51,22 +49,12 @@ void set_socket_nonblocking(int socket);
typedef
enum
{
FN_ERROR
,
FN_NOENT
,
FN_FILE
,
FN_DIR
}
file_status_t
;
/* Return FN_ERROR if filename can't be read, FN_NOENT if it doesn't
* exist, FN_FILE if it is a regular file, or FN_DIR if it's a
* directory. */
file_status_t
file_status
(
const
char
*
filename
);
/* Check whether dirname exists and is private. If yes returns
* 0. Else returns -1.
*/
int
check_private_dir
(
const
char
*
dirname
,
int
create
);
int
write_str_to_file
(
const
char
*
fname
,
const
char
*
str
);
char
*
read_file_to_str
(
const
char
*
filename
);
int
parse_line_from_file
(
char
*
line
,
int
maxlen
,
FILE
*
f
,
char
**
key_out
,
char
**
value_out
);
/* Minimalist interface to run a void function in the background. On
unix calls fork, on win32 calls beginthread. Returns -1 on failure.
func should not return, but rather should call spawn_exit.
*/
int
spawn_func
(
int
(
*
func
)(
void
*
),
void
*
data
);
void
spawn_exit
();
...
...
src/or/circuit.c
View file @
be874358
...
...
@@ -58,14 +58,11 @@ void circuit_remove(circuit_t *circ) {
circuit_t
*
circuit_new
(
aci_t
p_aci
,
connection_t
*
p_conn
)
{
circuit_t
*
circ
;
struct
timeval
now
;
my_gettimeofday
(
&
now
);
circ
=
(
circuit_t
*
)
tor_malloc
(
sizeof
(
circuit_t
));
memset
(
circ
,
0
,
sizeof
(
circuit_t
));
/* zero it out */
circ
->
timestamp_created
=
now
.
tv_sec
;
circ
->
timestamp_created
=
time
(
NULL
)
;
circ
->
p_aci
=
p_aci
;
circ
->
p_conn
=
p_conn
;
...
...
src/or/command.c
View file @
be874358
...
...
@@ -25,11 +25,11 @@ static void command_time_process_cell(cell_t *cell, connection_t *conn,
*
num
+=
1
;
my
_gettimeofday
(
&
start
);
tor
_gettimeofday
(
&
start
);
(
*
func
)(
cell
,
conn
);
my
_gettimeofday
(
&
end
);
tor
_gettimeofday
(
&
end
);
time_passed
=
tv_udiff
(
&
start
,
&
end
)
;
if
(
time_passed
>
5000
)
{
/* more than 5ms */
...
...
@@ -38,17 +38,13 @@ static void command_time_process_cell(cell_t *cell, connection_t *conn,
*
time
+=
time_passed
;
}
void
command_process_cell
(
cell_t
*
cell
,
connection_t
*
conn
)
{
static
int
num_create
=
0
,
num_created
=
0
,
num_relay
=
0
,
num_destroy
=
0
;
static
int
create_time
=
0
,
created_time
=
0
,
relay_time
=
0
,
destroy_time
=
0
;
static
long
current_second
=
0
;
/* from previous calls to gettimeofday */
struct
timeval
now
;
my_gettimeofday
(
&
now
);
static
time_t
current_second
=
0
;
/* from previous calls to time */
time_t
now
=
time
(
NULL
);
if
(
now
.
tv_sec
>
current_second
)
{
/* the second has rolled over */
if
(
now
>
current_second
)
{
/* the second has rolled over */
/* print stats */
log
(
LOG_INFO
,
"At end of second:"
);
log
(
LOG_INFO
,
"Create: %d (%d ms)"
,
num_create
,
create_time
/
1000
);
...
...
@@ -61,7 +57,7 @@ void command_process_cell(cell_t *cell, connection_t *conn) {
create_time
=
created_time
=
relay_time
=
destroy_time
=
0
;
/* remember which second it is, for next time */
current_second
=
now
.
tv_sec
;
current_second
=
now
;
}
switch
(
cell
->
command
)
{
...
...
src/or/config.c
View file @
be874358
...
...
@@ -61,8 +61,8 @@ static struct config_line *config_get_commandlines(int argc, char **argv) {
s
=
argv
[
i
];
while
(
*
s
==
'-'
)
s
++
;
new
->
key
=
strdup
(
s
);
new
->
value
=
strdup
(
argv
[
i
+
1
]);
new
->
key
=
tor_
strdup
(
s
);
new
->
value
=
tor_
strdup
(
argv
[
i
+
1
]);
log
(
LOG_DEBUG
,
"Commandline: parsed keyword '%s', value '%s'"
,
new
->
key
,
new
->
value
);
...
...
@@ -85,8 +85,8 @@ static struct config_line *config_get_lines(FILE *f) {
while
(
(
result
=
parse_line_from_file
(
line
,
sizeof
(
line
),
f
,
&
key
,
&
value
))
>
0
)
{
new
=
tor_malloc
(
sizeof
(
struct
config_line
));
new
->
key
=
strdup
(
key
);
new
->
value
=
strdup
(
value
);
new
->
key
=
tor_
strdup
(
key
);
new
->
value
=
tor_
strdup
(
value
);
new
->
next
=
front
;
front
=
new
;
...
...
@@ -131,7 +131,7 @@ static int config_compare(struct config_line *c, char *key, int type, void *arg)
*
(
int
*
)
arg
=
i
;
break
;
case
CONFIG_TYPE_STRING
:
*
(
char
**
)
arg
=
strdup
(
c
->
value
);
*
(
char
**
)
arg
=
tor_
strdup
(
c
->
value
);
break
;
case
CONFIG_TYPE_DOUBLE
:
*
(
double
*
)
arg
=
atof
(
c
->
value
);
...
...
src/or/connection.c
View file @
be874358
...
...
@@ -73,9 +73,7 @@ static int connection_init_accepted_conn(connection_t *conn);
connection_t
*
connection_new
(
int
type
)
{
connection_t
*
conn
;
struct
timeval
now
;
my_gettimeofday
(
&
now
);
time_t
now
=
time
(
NULL
);
conn
=
(
connection_t
*
)
tor_malloc
(
sizeof
(
connection_t
));
memset
(
conn
,
0
,
sizeof
(
connection_t
));
/* zero it out to start */
...
...
@@ -84,9 +82,9 @@ connection_t *connection_new(int type) {
conn
->
inbuf
=
buf_new
();
conn
->
outbuf
=
buf_new
();
conn
->
timestamp_created
=
now
.
tv_sec
;
conn
->
timestamp_lastread
=
now
.
tv_sec
;
conn
->
timestamp_lastwritten
=
now
.
tv_sec
;
conn
->
timestamp_created
=
now
;
conn
->
timestamp_lastread
=
now
;
conn
->
timestamp_lastwritten
=
now
;
return
conn
;
}
...
...
@@ -195,7 +193,7 @@ int connection_handle_listener_read(connection_t *conn, int new_type) {
newconn
=
connection_new
(
new_type
);
newconn
->
s
=
news
;
newconn
->
address
=
strdup
(
inet_ntoa
(
remote
.
sin_addr
));
/* remember the remote address */
newconn
->
address
=
tor_
strdup
(
inet_ntoa
(
remote
.
sin_addr
));
/* remember the remote address */
newconn
->
addr
=
ntohl
(
remote
.
sin_addr
.
s_addr
);
newconn
->
port
=
ntohs
(
remote
.
sin_port
);
...
...
@@ -309,10 +307,8 @@ int retry_all_connections(uint16_t or_listenport, uint16_t ap_listenport, uint16
}
int
connection_handle_read
(
connection_t
*
conn
)
{
struct
timeval
now
;
my_gettimeofday
(
&
now
);
conn
->
timestamp_lastread
=
now
.
tv_sec
;
conn
->
timestamp_lastread
=
time
(
NULL
);
switch
(
conn
->
type
)
{
case
CONN_TYPE_OR_LISTENER
:
...
...
@@ -433,15 +429,13 @@ int connection_flush_buf(connection_t *conn) {
/* return -1 if you want to break the conn, else return 0 */
int
connection_handle_write
(
connection_t
*
conn
)
{
struct
timeval
now
;
if
(
connection_is_listener
(
conn
))
{
log_fn
(
LOG_WARNING
,
"Got a listener socket. Can't happen!"
);
return
-
1
;
}
my_gettimeofday
(
&
now
);
conn
->
timestamp_lastwritten
=
now
.
tv_sec
;
conn
->
timestamp_lastwritten
=
time
(
NULL
);
if
(
connection_speaks_cells
(
conn
)
&&
conn
->
state
!=
OR_CONN_STATE_CONNECTING
)
{
if
(
conn
->
state
==
OR_CONN_STATE_HANDSHAKING
)
{
...
...
src/or/connection_edge.c
View file @
be874358
...
...
@@ -577,7 +577,7 @@ static int connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
n_stream
=
connection_new
(
CONN_TYPE_EXIT
);
memcpy
(
n_stream
->
stream_id
,
cell
->
payload
+
RELAY_HEADER_SIZE
,
STREAM_ID_SIZE
);
n_stream
->
address
=
strdup
(
cell
->
payload
+
RELAY_HEADER_SIZE
+
STREAM_ID_SIZE
);
n_stream
->
address
=
tor_
strdup
(
cell
->
payload
+
RELAY_HEADER_SIZE
+
STREAM_ID_SIZE
);
n_stream
->
port
=
atoi
(
colon
+
1
);
n_stream
->
state
=
EXIT_CONN_STATE_RESOLVING
;
n_stream
->
s
=
-
1
;
/* not yet valid */
...
...
src/or/connection_or.c
View file @
be874358
...
...
@@ -83,10 +83,10 @@ void connection_or_init_conn_from_router(connection_t *conn, routerinfo_t *route
conn
->
onion_pkey
=
crypto_pk_dup_key
(
router
->
onion_pkey
);
conn
->
link_pkey
=
crypto_pk_dup_key
(
router
->
link_pkey
);
conn
->
identity_pkey
=
crypto_pk_dup_key
(
router
->
identity_pkey
);
conn
->
nickname
=
strdup
(
router
->
nickname
);
conn
->
nickname
=
tor_
strdup
(
router
->
nickname
);
if
(
conn
->
address
)
free
(
conn
->
address
);
conn
->
address
=
strdup
(
router
->
address
);
conn
->
address
=
tor_
strdup
(
router
->
address
);
}
connection_t
*
connection_or_connect
(
routerinfo_t
*
router
)
{
...
...
src/or/cpuworker.c
View file @
be874358
...
...
@@ -184,7 +184,7 @@ static int spawn_cpuworker(void) {
/* set up conn so it's got all the data we need to remember */
conn
->
s
=
fd
[
0
];
conn
->
address
=
strdup
(
"localhost"
);
conn
->
address
=
tor_
strdup
(
"localhost"
);
if
(
connection_add
(
conn
)
<
0
)
{
/* no space, forget it */
log_fn
(
LOG_WARNING
,
"connection_add failed. Giving up."
);
...
...
src/or/directory.c
View file @
be874358
...
...
@@ -45,8 +45,8 @@ void directory_initiate_command(routerinfo_t *router, int command) {
/* set up conn so it's got all the data we need to remember */
conn
->
addr
=
router
->
addr
;
conn
->
port
=
router
->
dir_port
;
conn
->
address
=
strdup
(
router
->
address
);
conn
->
nickname
=
strdup
(
router
->
nickname
);
conn
->
address
=
tor_
strdup
(
router
->
address
);
conn
->
nickname
=
tor_
strdup
(
router
->
nickname
);
if
(
router
->
identity_pkey
)
conn
->
identity_pkey
=
crypto_pk_dup_key
(
router
->
identity_pkey
);
else
{
...
...
src/or/dirserv.c
View file @
be874358
...
...
@@ -29,12 +29,12 @@ add_fingerprint_to_dir(const char *nickname, const char *fp)
for
(
i
=
0
;
i
<
n_fingerprints
;
++
i
)
{
if
(
!
strcasecmp
(
fingerprint_list
[
i
].
nickname
,
nickname
))
{
free
(
fingerprint_list
[
i
].
fingerprint
);
fingerprint_list
[
i
].
fingerprint
=
strdup
(
fp
);
fingerprint_list
[
i
].
fingerprint
=
tor_
strdup
(
fp
);
return
;
}
}
fingerprint_list
[
n_fingerprints
].
nickname
=
strdup
(
nickname
);
fingerprint_list
[
n_fingerprints
].
fingerprint
=
strdup
(
fp
);
fingerprint_list
[
n_fingerprints
].
nickname
=
tor_
strdup
(
nickname
);
fingerprint_list
[
n_fingerprints
].
fingerprint
=
tor_
strdup
(
fp
);
++
n_fingerprints
;
}
...
...
@@ -83,8 +83,8 @@ dirserv_parse_fingerprint_file(const char *fname)
}
}
if
(
i
==
n_fingerprints_tmp
)
{
/* not a duplicate */
fingerprint_list_tmp
[
n_fingerprints_tmp
].
nickname
=
strdup
(
nickname
);
fingerprint_list_tmp
[
n_fingerprints_tmp
].
fingerprint
=
strdup
(
fingerprint
);
fingerprint_list_tmp
[
n_fingerprints_tmp
].
nickname
=
tor_
strdup
(
nickname
);
fingerprint_list_tmp
[
n_fingerprints_tmp
].
fingerprint
=
tor_
strdup
(
fingerprint
);
++
n_fingerprints_tmp
;
}
}
...
...
@@ -427,7 +427,7 @@ size_t dirserv_get_directory(const char **directory)
/* Now read the directory we just made in order to update our own
* router lists. This does more signature checking than is strictly
* necessary, but safe is better than sorry. */
new_directory
=
strdup
(
the_directory
);
new_directory
=
tor_
strdup
(
the_directory
);
/* use a new copy of the dir, since get_dir_from_string scribbles on it */
if
(
router_get_dir_from_string
(
new_directory
,
get_identity_key
()))
{
log_fn
(
LOG_ERR
,
"We just generated a directory we can't parse. Dying."
);
...
...
src/or/dns.c
View file @
be874358
...
...
@@ -165,7 +165,7 @@ static int assign_to_dnsworker(connection_t *exitconn) {
return
-
1
;
}
dnsconn
->
address
=
strdup
(
exitconn
->
address
);
dnsconn
->
address
=
tor_
strdup
(
exitconn
->
address
);
dnsconn
->
state
=
DNSWORKER_STATE_BUSY
;
num_dnsworkers_busy
++
;
...
...
@@ -381,7 +381,7 @@ static int spawn_dnsworker(void) {
/* set up conn so it's got all the data we need to remember */
conn
->
s
=
fd
[
0
];
conn
->
address
=
strdup
(
"localhost"
);
conn
->
address
=
tor_
strdup
(
"localhost"
);
if
(
connection_add
(
conn
)
<
0
)
{
/* no space, forget it */
log_fn
(
LOG_WARNING
,
"connection_add failed. Giving up."
);
...
...
src/or/main.c
View file @
be874358
...
...
@@ -270,7 +270,7 @@ static int prepare_for_poll(void) {
cell_t
cell
;
circuit_t
*
circ
;
my
_gettimeofday
(
&
now
);
tor
_gettimeofday
(
&
now
);
if
(
now
.
tv_sec
>
current_second
)
{
/* the second has rolled over. check more stuff. */
...
...
@@ -656,23 +656,22 @@ static void catch(int the_signal) {
static
void
dumpstats
(
void
)
{
/* dump stats to stdout */
int
i
;
connection_t
*
conn
;
struct
time
val
now
;
time
_t
now
=
time
(
NULL
)
;
printf
(
"Dumping stats:
\n
"
);
my_gettimeofday
(
&
now
);
for
(
i
=
0
;
i
<
nfds
;
i
++
)
{
conn
=
connection_array
[
i
];
printf
(
"Conn %d (socket %d) type %d (%s), state %d (%s), created %ld secs ago
\n
"
,
i
,
conn
->
s
,
conn
->
type
,
conn_type_to_string
[
conn
->
type
],
conn
->
state
,
conn_state_to_string
[
conn
->
type
][
conn
->
state
],
now
.
tv_sec
-
conn
->
timestamp_created
);
conn
->
state
,
conn_state_to_string
[
conn
->
type
][
conn
->
state
],
now
-
conn
->
timestamp_created
);
if
(
!
connection_is_listener
(
conn
))
{
printf
(
"Conn %d is to '%s:%d'.
\n
"
,
i
,
conn
->
address
,
conn
->
port
);
printf
(
"Conn %d: %d bytes waiting on inbuf (last read %ld secs ago)
\n
"
,
i
,
(
int
)
buf_datalen
(
conn
->
inbuf
),
now
.
tv_sec
-
conn
->
timestamp_lastread
);
printf
(
"Conn %d: %d bytes waiting on outbuf (last written %ld secs ago)
\n
"
,
i
,
(
int
)
buf_datalen
(
conn
->
outbuf
),
now
.
tv_sec
-
conn
->
timestamp_lastwritten
);
now
-
conn
->
timestamp_lastread
);
printf
(
"Conn %d: %d bytes waiting on outbuf (last written %ld secs ago)
\n
"
,
i
,
(
int
)
buf_datalen
(
conn
->
outbuf
),
now
-
conn
->
timestamp_lastwritten
);
}
circuit_dump_by_conn
(
conn
);
/* dump info about all the circuits using this conn */
printf
(
"
\n
"
);
...
...
@@ -703,7 +702,6 @@ static void dumpstats(void) { /* dump stats to stdout */
if
(
stats_n_seconds_reading
)
printf
(
"Average bandwidth used: %d bytes/sec
\n
"
,
(
int
)
(
stats_n_bytes_read
/
stats_n_seconds_reading
));
}
void
daemonize
(
void
)
{
...
...
src/or/routers.c
View file @
be874358
...
...
@@ -606,7 +606,7 @@ int router_get_dir_from_string_impl(char *s, directory_t **dest,
log_fn
(
LOG_WARNING
,
"Invalid recommended-software line"
);
goto
err
;
}
versions
=
strdup
(
tok
.
val
.
cmd
.
args
[
0
]);
versions
=
tor_
strdup
(
tok
.
val
.
cmd
.
args
[
0
]);
NEXT_TOK
();
TOK_IS
(
K_RUNNING_ROUTERS
,
"running-routers"
);
...
...
@@ -801,8 +801,7 @@ routerinfo_t *router_get_entry_from_string(char**s) {
log_fn
(
LOG_WARNING
,
"Wrong # of arguments to
\"
router
\"
"
);
goto
err
;
}
if
(
!
(
router
->
nickname
=
strdup
(
ARGS
[
0
])))
goto
err
;
router
->
nickname
=
tor_strdup
(
ARGS
[
0
]);
if
(
strlen
(
router
->
nickname
)
>
MAX_NICKNAME_LEN
)
{
log_fn
(
LOG_WARNING
,
"Router nickname too long."
);
goto
err
;
...
...
@@ -814,8 +813,7 @@ routerinfo_t *router_get_entry_from_string(char**s) {
}
/* read router.address */
if
(
!
(
router
->
address
=
strdup
(
ARGS
[
1
])))
goto
err
;
router
->
address
=
tor_strdup
(
ARGS
[
1
]);
router
->
addr
=
0
;
/* Read router->or_port */
...
...
@@ -975,8 +973,8 @@ static int router_add_exit_policy(routerinfo_t *router,
if
(
!
colon
)
goto
policy_read_failed
;
*
colon
=
0
;
newe
->
address
=
strdup
(
arg
);
newe
->
port
=
strdup
(
colon
+
1
);
newe
->
address
=
tor_
strdup
(
arg
);
newe
->
port
=
tor_
strdup
(
colon
+
1
);
log_fn
(
LOG_DEBUG
,
"%s %s:%s"
,
newe
->
policy_type
==
EXIT_POLICY_REJECT
?
"reject"
:
"accept"
,
...
...
@@ -1064,8 +1062,8 @@ int router_rebuild_descriptor(void) {
address
=
localhostname
;
}
ri
=
tor_malloc
(
sizeof
(
routerinfo_t
));
ri
->
address
=
strdup
(
address
);
ri
->
nickname
=
strdup
(
options
.
Nickname
);
ri
->
address
=
tor_
strdup
(
address
);
ri
->
nickname
=
tor_
strdup
(
options
.
Nickname
);
/* No need to set addr. */
ri
->
or_port
=
options
.
ORPort
;
ri
->
ap_port
=
options
.
APPort
;
...
...
@@ -1205,7 +1203,7 @@ int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
s
[
written
+
1
]
=
0
;
#ifdef DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
s_tmp
=
s_dup
=
strdup
(
s
);
s_tmp
=
s_dup
=
tor_
strdup
(
s
);
ri_tmp
=
router_get_entry_from_string
(
&
s_tmp
);
if
(
!
ri_tmp
)
{
log_fn
(
LOG_ERR
,
"We just generated a router descriptor we can't parse: <<%s>>"
,
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment