From: erik quanstrom <quanstro@quanstro.net>
To: 9fans@9fans.net
Subject: Re: [9fans] bug or feature ? --- ip/ping -6
Date: Fri, 1 Jan 2016 17:12:46 -0800 [thread overview]
Message-ID: <eb60c30ea8e2c07e6f82ddc88688267c@mule> (raw)
In-Reply-To: <2F96B09F-7524-473A-B883-9A7B7DD09978@ar.aichi-u.ac.jp>
[-- Attachment #1: Type: text/plain, Size: 6817 bytes --]
on reading the man page, i found a small flaw in the implementation. according to
the man page, -6 forces is of icmp6, even if the address is icmp4. i changed ping to
do that.as a result, i added a -4 flag which forces the ping to use icmp4. obviously, there is no
native 6-in-4, so this is an error.
also, i corrected an indirection of a nil pointer when a name lookup fails.
my pathetic excuse for unit testing, and a diff are below. my version (hacks and all)
is attached.
- erik
ps. the hand-implementation of csquery seems to be a result of not trusting
ndb/cs to be running. odd. does anyone have context on why this would be useful?
---
; ip/ping -6an1 bwc
sending 1 64 byte messages 1000 ms apart to icmpv6!2402:6b00:22cd:bf80::9!1
2402:6b00:22cd:bf80::7 -> 2402:6b00:22cd:bf80::9
0: 2402:6b00:22cd:bf80::9 -> 2402:6b00:22cd:bf80::7 rtt 78 µs, avg rtt 78 µs, ttl = 255
; ip/ping -4an1 bwc
sending 1 64 byte messages 1000 ms apart to icmp!10.1.1.9!1
10.1.1.7 -> 10.1.1.9
0: 10.1.1.9 -> 10.1.1.7 rtt 70 µs, avg rtt 70 µs, ttl = 255
; ip/ping -an1 bwc
sending 1 64 byte messages 1000 ms apart to icmp!10.1.1.9!1
10.1.1.7 -> 10.1.1.9
0: 10.1.1.9 -> 10.1.1.7 rtt 72 µs, avg rtt 72 µs, ttl = 255
; ip/ping -4an1 10.1.1.7
sending 1 64 byte messages 1000 ms apart to icmp!10.1.1.7!1
10.1.1.7 -> 10.1.1.7
0: 10.1.1.7 -> 10.1.1.7 rtt 15 µs, avg rtt 15 µs, ttl = 255
; ip/ping -6an1 10.1.1.7
sending 1 64 byte messages 1000 ms apart to icmpv6!10.1.1.7!1
2402:6b00:22cd:bf80::7 -> 10.1.1.7
0: 10.1.1.7 -> 10.1.1.7 rtt 18 µs, avg rtt 18 µs, ttl = 255
; ip/ping -4an1 2402:6b00:22cd:bf80::7
ip/ping: ip/ping: nametoip: address 2402:6b00:22cd:bf80::7 does not match proto 4
; ip/ping -an1 missing
ip/ping: cannot write tcp!missing!1 to/net/cs: cs: can't translate address: dns: resource does not exist; negrcode
; diffy -c ping.c
/n/dump/2016/0101/sys/src/cmd/ip/ping.c:391,505 - ping.c:391,461
return colon;
}
- /* from /sys/src/libc/9sys/dial.c */
-
- enum
+ char*
+ nametoip(char *cs, char *name, int *ipver)
{
- Maxstring = 128,
- Maxpath = 256,
- };
+ int n,fd;
+ char buf[128], ip6[128], ip4[128], *p, *addr, *ip;
- typedef struct DS DS;
- struct DS {
- /* dist string */
- char buf[Maxstring];
- char *netdir;
- char *proto;
- char *rem;
-
- /* other args */
- char *local;
- char *dir;
- int *cfdp;
- };
-
- /*
- * parse a dial string
- */
- static void
- _dial_string_parse(char *str, DS *ds)
- {
- char *p, *p2;
-
- strncpy(ds->buf, str, Maxstring);
- ds->buf[Maxstring-1] = 0;
-
- p = strchr(ds->buf, '!');
- if(p == 0) {
- ds->netdir = 0;
- ds->proto = "net";
- ds->rem = ds->buf;
- } else {
- if(*ds->buf != '/' && *ds->buf != '#'){
- ds->netdir = 0;
- ds->proto = ds->buf;
- } else {
- for(p2 = p; *p2 != '/'; p2--)
- ;
- *p2++ = 0;
- ds->netdir = ds->buf;
- ds->proto = p2;
- }
- *p = 0;
- ds->rem = p + 1;
+ ip6[0] = 0;
+ ip4[0] = 0;
+ if(isdottedquad(name)){
+ snprint(ip4, sizeof ip4, "%s", name);
+ goto match;
}
- }
+ if(isv6lit(name)){
+ snprint(ip6, sizeof ip6, "%s", name);
+ goto match;
+ }
- /* end excerpt from /sys/src/libc/9sys/dial.c */
-
- /* side effect: sets network & target */
- static int
- isv4name(char *name)
- {
- int r = 1;
- char *root, *ip, *pr;
- DS ds;
-
- _dial_string_parse(name, &ds);
-
- /* cope with leading /net.alt/icmp! and the like */
- root = nil;
- if (ds.netdir != nil) {
- pr = strrchr(ds.netdir, '/');
- if (pr == nil)
- pr = ds.netdir;
- else {
- *pr++ = '\0';
- root = ds.netdir;
- network = strdup(root);
+ if(cs == nil)
+ cs = "/net/cs";
+ fd = open(cs, ORDWR);
+ if(fd < 0)
+ sysfatal("cannot open %s: %r", cs);
+ addr = smprint("tcp!%s!1", name);
+ if(write(fd, addr, strlen(addr)) != strlen(addr)){
+ close(fd);
+ sysfatal("cannot write %s to%s: %r", addr, cs);
+ }
+ free(addr);
+ seek(fd, 0, 0);
+ while((n = read(fd, buf, sizeof(buf)-1)) > 0){
+ buf[n] = 0;
+ ip = strchr(buf,' ');
+ ip++;
+ p = strchr(ip,'!');
+ *p = 0;
+ if(isdottedquad(ip)){
+ if(ip4[0] == 0)
+ snprint(ip4, sizeof ip4, "%s", ip);
+ }else if(isv6lit(ip)){
+ if(ip6[0] == 0)
+ snprint(ip6, sizeof ip6, "%s", ip);
}
- if (strcmp(pr, v4pr.net) == 0)
- return 1;
- if (strcmp(pr, v6pr.net) == 0)
- return 0;
}
+ close(fd);
- /* if it's a literal, it's obvious from syntax which proto it is */
- free(target);
- target = strdup(ds.rem);
- if (isdottedquad(ds.rem))
- return 1;
- else if (isv6lit(ds.rem))
- return 0;
+ match:
+ if((*ipver == 4 || *ipver == -1) && ip4[0] != 0){
+ *ipver = 4;
+ return strdup(ip4);
+ }else if((*ipver == 6 || *ipver == -1) && ip6[0] != 0){
+ *ipver = 6;
+ return strdup(ip6);
+ }else if(*ipver == 6 && ip4[0] != 0)
+ return strdup(ip4);
+ else if(ip4[0] != 0 || ip6[0] != 0)
+ werrstr("address %s does not match proto %d", ip4[0]? ip4: ip6, *ipver);
- /* map name to ip and look at its syntax */
- ip = csgetvalue(root, "sys", ds.rem, "ip", nil);
- if (ip == nil)
- ip = csgetvalue(root, "dom", ds.rem, "ip", nil);
- if (ip == nil)
- ip = csgetvalue(root, "sys", ds.rem, "ipv6", nil);
- if (ip == nil)
- ip = csgetvalue(root, "dom", ds.rem, "ipv6", nil);
- if (ip != nil)
- r = isv4name(ip);
- free(ip);
- return r;
+ *ipver = -1;
+ return nil;
}
void
main(int argc, char **argv)
{
- int fd, msglen, interval, nmsg;
+ int fd, msglen, interval, nmsg, ipver;
char *ds;
nsecfd = bintime(-1, nil, nil, nil);
/n/dump/2016/0101/sys/src/cmd/ip/ping.c:509,517 - ping.c:465,475
msglen = interval = 0;
nmsg = MAXMSG;
+ ipver = -1;
ARGBEGIN {
+ case '4':
case '6':
- proto = &v6pr;
+ ipver = ARGC() - '0';
break;
case 'a':
addresses = 1;
/n/dump/2016/0101/sys/src/cmd/ip/ping.c:568,576 - ping.c:526,537
notify(catch);
- if (!isv4name(argv[0]))
- proto = &v6pr;
- ds = netmkaddr(argv[0], proto->net, "1");
+ target = nametoip(nil, argv[0], &ipver);
+ if(target == nil)
+ sysfatal("%s: nametoip: %r", argv0);
+ proto = ipver==4? &v4pr: &v6pr;
+
+ ds = netmkaddr(target, proto->net, "1");
fd = dial(ds, 0, 0, 0);
if(fd < 0){
fprint(2, "%s: couldn't dial %s: %r\n", argv0, ds);
/n/dump/2016/0101/sys/src/cmd/ip/ping.c:583,590 - ping.c:544,550
switch(rfork(RFPROC|RFMEM|RFFDG)){
case -1:
- fprint(2, "%s: can't fork: %r\n", argv0);
- /* fallthrough */
+ sysfatal("%s: can't fork: %r\n", argv0);
case 0:
rcvr(fd, msglen, interval, nmsg);
exits(0);
[-- Attachment #2: ping.c --]
[-- Type: text/plain, Size: 11200 bytes --]
/* ping for ip v4 and v6 */
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "icmp.h"
enum {
MAXMSG = 32,
SLEEPMS = 1000,
SECOND = 1000000000LL,
MINUTE = 60*SECOND,
};
typedef struct Req Req;
struct Req
{
ushort seq; /* sequence number */
vlong time; /* time sent */
vlong rtt;
int ttl;
int replied;
Req *next;
};
typedef struct {
int version;
char *net;
int echocmd;
int echoreply;
unsigned iphdrsz;
void (*prreply)(Req *r, void *v);
void (*prlost)(ushort seq, void *v);
} Proto;
Req *first; /* request list */
Req *last; /* ... */
Lock listlock;
char *argv0;
int addresses;
int debug;
int done;
int flood;
int lostmsgs;
int lostonly;
int quiet;
int rcvdmsgs;
int rint;
int nsecfd;
ushort firstseq;
vlong sum;
int waittime = 5000;
static char *network, *target;
void lost(Req*, void*);
void reply(Req*, void*);
static void
usage(void)
{
fprint(2,
"usage: %s [-6alq] [-s msgsize] [-i millisecs] [-n #pings] dest\n",
argv0);
exits("usage");
}
static void
catch(void *a, char *msg)
{
USED(a);
if(strstr(msg, "alarm"))
noted(NCONT);
else if(strstr(msg, "die"))
exits("errors");
else
noted(NDFLT);
}
static void
prlost4(ushort seq, void *v)
{
Ip4hdr *ip4 = v;
print("lost %ud: %V -> %V\n", seq, ip4->src, ip4->dst);
}
static void
prlost6(ushort seq, void *v)
{
Ip6hdr *ip6 = v;
print("lost %ud: %I -> %I\n", seq, ip6->src, ip6->dst);
}
static void
prreply4(Req *r, void *v)
{
Ip4hdr *ip4 = v;
print("%ud: %V -> %V rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
r->seq - firstseq, ip4->src, ip4->dst, r->rtt, sum/rcvdmsgs,
r->ttl);
}
static void
prreply6(Req *r, void *v)
{
Ip6hdr *ip6 = v;
print("%ud: %I -> %I rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
r->seq - firstseq, ip6->src, ip6->dst, r->rtt, sum/rcvdmsgs,
r->ttl);
}
static Proto v4pr = {
4, "icmp",
EchoRequest, EchoReply,
IPV4HDR_LEN,
prreply4, prlost4,
};
static Proto v6pr = {
6, "icmpv6",
EchoRequestV6, EchoReplyV6,
IPV6HDR_LEN,
prreply6, prlost6,
};
static Proto *proto = &v4pr;
Icmphdr *
geticmp(void *v)
{
char *p = v;
return (Icmphdr *)(p + proto->iphdrsz);
}
void
clean(ushort seq, vlong now, void *v)
{
int ttl;
Req **l, *r, *rlost, **rr;
ttl = 0;
if (v) {
if (proto->version == 4)
ttl = ((Ip4hdr *)v)->ttl;
else
ttl = ((Ip6hdr *)v)->ttl;
}
rlost = nil;
rr = &rlost;
lock(&listlock);
last = nil;
for(l = &first; *l; ){
r = *l;
if(v && r->seq == seq){
r->rtt = now-r->time;
r->ttl = ttl;
reply(r, v);
}
if(now-r->time >= MINUTE){
*l = r->next;
r->next = nil;
*rr = r;
rr = &(*rr)->next;
}else{
last = r;
l = &r->next;
}
}
unlock(&listlock);
for(; (r = rlost) != nil;){
rlost = rlost->next;
r->rtt = now-r->time;
if(v)
r->ttl = ttl;
if(r->replied == 0)
lost(r, v);
free(r);
}
}
static uchar loopbacknet[IPaddrlen] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0xff, 0xff,
127, 0, 0, 0
};
static uchar loopbackmask[IPaddrlen] = {
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0, 0, 0
};
/*
* find first ip addr suitable for proto and
* that isn't the friggin loopback address.
* deprecate link-local and multicast addresses.
*/
static int
myipvnaddr(uchar *ip, Proto *proto, char *net)
{
int ipisv4, wantv4;
Ipifc *nifc;
Iplifc *lifc;
uchar mynet[IPaddrlen], linklocal[IPaddrlen];
static Ipifc *ifc;
ipmove(linklocal, IPnoaddr);
wantv4 = proto->version == 4;
ifc = readipifc(net, ifc, -1);
for(nifc = ifc; nifc; nifc = nifc->next)
for(lifc = nifc->lifc; lifc; lifc = lifc->next){
maskip(lifc->ip, loopbackmask, mynet);
if(ipcmp(mynet, loopbacknet) == 0)
continue;
if(ISIPV6MCAST(lifc->ip) || ISIPV6LINKLOCAL(lifc->ip)) {
ipmove(linklocal, lifc->ip);
continue;
}
ipisv4 = isv4(lifc->ip) != 0;
if(ipcmp(lifc->ip, IPnoaddr) != 0 && wantv4 == ipisv4){
ipmove(ip, lifc->ip);
return 0;
}
}
/* no global unicast addrs found, fall back to link-local, if any */
ipmove(ip, linklocal);
return ipcmp(ip, IPnoaddr) == 0? -1: 0;
}
void
sender(int fd, int msglen, int interval, int n)
{
int i, extra;
ushort seq;
char buf[64*1024+512];
uchar me[IPaddrlen], mev4[IPv4addrlen];
Icmphdr *icmp;
Req *r;
srand(time(0));
firstseq = seq = rand();
icmp = geticmp(buf);
memset(buf, 0, proto->iphdrsz + ICMP_HDRSIZE);
for(i = proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++)
buf[i] = i;
icmp->type = proto->echocmd;
icmp->code = 0;
/* arguably the kernel should fill in the right src addr. */
myipvnaddr(me, proto, network);
if (proto->version == 4) {
v6tov4(mev4, me);
memmove(((Ip4hdr *)buf)->src, mev4, IPv4addrlen);
} else
ipmove(((Ip6hdr *)buf)->src, me);
if (addresses)
print("\t%I -> %s\n", me, target);
if(rint != 0 && interval <= 0)
rint = 0;
extra = 0;
for(i = 0; i < n; i++){
if(i != 0){
if(rint != 0)
extra = nrand(interval);
sleep(interval + extra);
}
r = malloc(sizeof *r);
if (r == nil)
continue;
hnputs(icmp->seq, seq);
r->seq = seq;
r->next = nil;
r->replied = 0;
// nsecfd = bintime(nsecfd, &r->time, nil, nil); /* avoid early free in reply! */
r->time = 1ull<<62;
lock(&listlock);
if(first == nil)
first = r;
else
last->next = r;
last = r;
unlock(&listlock);
nsecfd = bintime(nsecfd, &r->time, nil, nil);
if(write(fd, buf, msglen) < msglen){
fprint(2, "%s: write failed: %r\n", argv0);
return;
}
seq++;
}
done = 1;
}
void
rcvr(int fd, int msglen, int interval, int nmsg)
{
int i, n, munged;
ushort x;
vlong now;
uchar buf[64*1024+512];
Icmphdr *icmp;
Req *r;
sum = 0;
while(lostmsgs+rcvdmsgs < nmsg){
alarm((nmsg-lostmsgs-rcvdmsgs)*interval+waittime);
// alarm(interval+waittime);
n = read(fd, buf, sizeof buf);
alarm(0);
nsecfd = bintime(nsecfd, &now, nil, nil);
if(n <= 0){ /* read interrupted - time to go */
clean(0, now+MINUTE, nil);
continue;
}
if(n < msglen){
print("bad len %d/%d\n", n, msglen);
continue;
}
icmp = geticmp(buf);
munged = 0;
for(i = proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++)
if(buf[i] != (uchar)i)
munged++;
if(munged)
print("corrupted reply\n");
x = nhgets(icmp->seq);
if(icmp->type != proto->echoreply || icmp->code != 0) {
print("bad type/code/sequence %d/%d/%d (want %d/%d/%d)\n",
icmp->type, icmp->code, x,
proto->echoreply, 0, x);
continue;
}
clean(x, now, buf);
}
lock(&listlock);
for(r = first; r; r = r->next)
if(r->replied == 0)
lostmsgs++;
unlock(&listlock);
if(!quiet && lostmsgs)
print("%d out of %d messages lost\n", lostmsgs,
lostmsgs+rcvdmsgs);
}
static int
isdottedquad(char *name)
{
int dot = 0, digit = 0;
for (; *name != '\0'; name++)
if (*name == '.')
dot++;
else if (isdigit(*name))
digit++;
else
return 0;
return dot && digit;
}
static int
isv6lit(char *name)
{
int colon = 0, hex = 0;
for (; *name != '\0'; name++)
if (*name == ':')
colon++;
else if (isxdigit(*name))
hex++;
else
return 0;
return colon;
}
char*
nametoip(char *cs, char *name, int *ipver)
{
int n,fd;
char buf[128], ip6[128], ip4[128], *p, *addr, *ip;
ip6[0] = 0;
ip4[0] = 0;
if(isdottedquad(name)){
snprint(ip4, sizeof ip4, "%s", name);
goto match;
}
if(isv6lit(name)){
snprint(ip6, sizeof ip6, "%s", name);
goto match;
}
if(cs == nil)
cs = "/net/cs";
fd = open(cs, ORDWR);
if(fd < 0)
sysfatal("cannot open %s: %r", cs);
addr = smprint("tcp!%s!1", name);
if(write(fd, addr, strlen(addr)) != strlen(addr)){
close(fd);
sysfatal("cannot write %s to%s: %r", addr, cs);
}
free(addr);
seek(fd, 0, 0);
while((n = read(fd, buf, sizeof(buf)-1)) > 0){
buf[n] = 0;
ip = strchr(buf,' ');
ip++;
p = strchr(ip,'!');
*p = 0;
if(isdottedquad(ip)){
if(ip4[0] == 0)
snprint(ip4, sizeof ip4, "%s", ip);
}else if(isv6lit(ip)){
if(ip6[0] == 0)
snprint(ip6, sizeof ip6, "%s", ip);
}
}
close(fd);
match:
if((*ipver == 4 || *ipver == -1) && ip4[0] != 0){
*ipver = 4;
return strdup(ip4);
}else if((*ipver == 6 || *ipver == -1) && ip6[0] != 0){
*ipver = 6;
return strdup(ip6);
}else if(*ipver == 6 && ip4[0] != 0)
return strdup(ip4);
else if(ip4[0] != 0 || ip6[0] != 0)
werrstr("address %s does not match proto %d", ip4[0]? ip4: ip6, *ipver);
*ipver = -1;
return nil;
}
void
main(int argc, char **argv)
{
int fd, msglen, interval, nmsg, ipver;
char *ds;
nsecfd = bintime(-1, nil, nil, nil);
fmtinstall('V', eipfmt);
fmtinstall('I', eipfmt);
msglen = interval = 0;
nmsg = MAXMSG;
ipver = -1;
ARGBEGIN {
case '4':
case '6':
ipver = ARGC() - '0';
break;
case 'a':
addresses = 1;
break;
case 'd':
debug++;
break;
case 'f':
flood = 1;
break;
case 'i':
interval = atoi(EARGF(usage()));
if(interval < 0)
usage();
break;
case 'l':
lostonly++;
break;
case 'n':
nmsg = atoi(EARGF(usage()));
if(nmsg < 0)
usage();
break;
case 'q':
quiet = 1;
break;
case 'r':
rint = 1;
break;
case 's':
msglen = atoi(EARGF(usage()));
break;
case 'w':
waittime = atoi(EARGF(usage()));
if(waittime < 0)
usage();
break;
default:
usage();
break;
} ARGEND;
if(msglen < proto->iphdrsz + ICMP_HDRSIZE)
msglen = proto->iphdrsz + ICMP_HDRSIZE;
if(msglen < 64)
msglen = 64;
if(msglen >= 64*1024)
msglen = 64*1024-1;
if(interval <= 0 && !flood)
interval = SLEEPMS;
if(argc < 1)
usage();
notify(catch);
target = nametoip(nil, argv[0], &ipver);
if(target == nil)
sysfatal("%s: nametoip: %r", argv0);
proto = ipver==4? &v4pr: &v6pr;
ds = netmkaddr(target, proto->net, "1");
fd = dial(ds, 0, 0, 0);
if(fd < 0){
fprint(2, "%s: couldn't dial %s: %r\n", argv0, ds);
exits("dialing");
}
if (!quiet)
print("sending %d %d byte messages %d ms apart to %s\n",
nmsg, msglen, interval, ds);
switch(rfork(RFPROC|RFMEM|RFFDG)){
case -1:
sysfatal("%s: can't fork: %r\n", argv0);
case 0:
rcvr(fd, msglen, interval, nmsg);
exits(0);
default:
sender(fd, msglen, interval, nmsg);
wait();
exits(lostmsgs ? "lost messages" : "");
}
}
void
reply(Req *r, void *v)
{
r->rtt /= 1000LL;
sum += r->rtt;
if(!r->replied)
rcvdmsgs++;
if(!quiet && !lostonly)
if(addresses)
(*proto->prreply)(r, v);
else
print("%ud: rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
r->seq - firstseq, r->rtt, sum/rcvdmsgs, r->ttl);
r->replied = 1;
}
void
lost(Req *r, void *v)
{
if(!quiet)
if(addresses && v != nil)
(*proto->prlost)(r->seq - firstseq, v);
else
print("lost %ud\n", r->seq - firstseq);
lostmsgs++;
}
next prev parent reply other threads:[~2016-01-02 1:12 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-12-28 9:04 arisawa
2015-12-29 0:10 ` Anthony Martin
2015-12-30 12:48 ` arisawa
2015-12-30 13:21 ` Charles Forsyth
2015-12-30 15:05 ` Steve Simon
2015-12-30 15:26 ` Kurt H Maier
2015-12-30 19:05 ` Steve Simon
2015-12-30 22:43 ` Kenny Lasse Hoff Levinsen
2015-12-30 23:36 ` Steve Simon
2015-12-31 0:16 ` Bakul Shah
2015-12-31 1:36 ` arisawa
2015-12-31 3:44 ` Dave Eckhardt
2016-01-02 1:12 ` erik quanstrom [this message]
2016-01-02 1:54 ` erik quanstrom
2016-01-03 19:58 ` hiro
2016-01-03 20:04 ` erik quanstrom
2016-01-03 20:31 ` erik quanstrom
2016-01-03 21:40 ` hiro
2016-01-03 21:43 ` hiro
2016-01-03 21:47 ` erik quanstrom
2016-01-04 1:31 ` erik quanstrom
2016-01-04 10:51 ` Anthony Martin
2016-01-04 13:04 ` erik quanstrom
2016-01-04 13:58 ` erik quanstrom
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=eb60c30ea8e2c07e6f82ddc88688267c@mule \
--to=quanstro@quanstro.net \
--cc=9fans@9fans.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).