1 /*
2  * Copyright 2015-2021 The Regents of the University of California
3  * All rights reserved.
4  *
5  * This file is part of Spoofer.
6  *
7  * Spoofer is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Spoofer is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Spoofer.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /****************************************************************************
22    Program:     $Id: messages.cc,v 1.56 2021/04/28 17:39:07 kkeys Exp $
23    Author:      Ken Keys, CAIDA
24    Date:        $Date: 2021/04/28 17:39:07 $
25    Description: Spoofer control channel message routines
26 ****************************************************************************/
27 #include "spoof.h"
28 
29 /**
30  * Like inet_ntop() for an address stored in a std::string (as in a spoofer
31  * protobuf).
32  *
33  * If the address size is incorrect for the family, the address will be dumped
34  * in hex, making this function safe to use even when the source of the
35  * message is untrusted.
36  **/
pb_ntop(family_t family,const std::string & addr,char * buf,size_t buflen)37 const char *pb_ntop(family_t family, const std::string &addr, char *buf, size_t buflen)
38 {
39     if (addr.size() == addrlen(family))
40 	return inet_ntop(family, addr.data(), buf, safe_int<unsigned>(buflen));
41     unsigned off = unsigned(snprintf(buf, buflen, "(bad addr/%ld: ", long(addr.size())));
42     for (unsigned i = 0; i < addr.size(); i++) {
43 	if (off + 5 > buflen && i < addr.size() - 2) {
44 	    off += unsigned(snprintf(buf+off, buflen-off, "..."));
45 	    break;
46 	}
47 	off += unsigned(snprintf(buf+off, buflen-off, "%02x", (uint8_t)addr.data()[i]));
48     }
49     snprintf(buf+off, buflen-off, ")");
50     return buf;
51 }
52 
53 /**
54  * Convenience variant of pb_ntop() that writes to a static buffer.
55  **/
pb_ntop(family_t family,const std::string & addr)56 const char *pb_ntop(family_t family, const std::string &addr)
57 {
58     static char buf[INET6_ADDRSTRLEN+1];
59     return pb_ntop(family, addr, buf, sizeof(buf));
60 }
61 
62 // Not necessarily the absolute maximum, but a typical maximum.
addrStrWidth(family_t family)63 static inline int addrStrWidth(family_t family) {
64     switch (family) {
65 	case AF_INET: return 15;
66 	case AF_INET6: return 32;
67 	default: return 0;
68     }
69 }
70 
printSpoofSchedule(int indent,const SpooferSpoofSchedule & sched,family_t family,bool verboseFlag)71 static void printSpoofSchedule(int indent, const SpooferSpoofSchedule &sched,
72     family_t family, bool verboseFlag)
73 {
74     printf("#%*s Spoof schedule (targets: %d)\n", indent, "", sched.item_size());
75     if (verboseFlag) {
76         for (int i = 0; i < sched.item_size(); i++) {
77 	    const SpooferSpoofSchedule::Item &item = sched.item(i);
78 	    time_t ts = safe_int<time_t>(item.timestamp());
79 	    printf("#%*s [%d] %s  ", indent+1, "", i,
80 		gmTimeStr(&ts)());
81 	    if (family == AF_INET6)
82 		printf("\n#%*s   ", indent+1, "");
83 	    printf("%-*s ", addrStrWidth(family), pb_ntop(family, item.srcip()));
84 	    printf("-> %-*s\n", addrStrWidth(family), pb_ntop(family, item.dstip()));
85 	    {
86 		printf("#%*s   seq: %s", indent+1, "", item.seqno().c_str());
87 		if (item.has_hmac() && item.hmac().size() > 0) {
88 		    printf("  HMAC: ");
89 		    binDump(item.hmac().data(), item.hmac().size(), FALSE);
90 		}
91 		printf("\n");
92 	    }
93         }
94     }
95 }
96 
printTracefilterSchedule(int indent,const SpooferTracefilterSchedule & sched,family_t family)97 static void printTracefilterSchedule(int indent,
98     const SpooferTracefilterSchedule &sched, family_t family)
99 {
100     printf("#%*s Tracefilter schedule (targets: %d)\n", indent, "", sched.item_size());
101     for (int i = 0; i < sched.item_size(); i++) {
102 	const SpooferTracefilterSchedule::Item &item = sched.item(i);
103         printf("#%*s  [%d] dist: %u  ", indent, "", i, item.dist());
104 	if (family == AF_INET6)
105 	    printf("\n#%*s    ", indent, "");
106 	printf("%-*s ", addrStrWidth(family), pb_ntop(family, item.srcip()));
107 	printf("-> %-*s\n", addrStrWidth(family), pb_ntop(family, item.dstip()));
108     }
109 }
110 
printTracerouteSchedule(int indent,const SpooferTracerouteSchedule & sched,family_t family)111 static void printTracerouteSchedule(int indent,
112     const SpooferTracerouteSchedule &sched, family_t family)
113 {
114     printf("#%*s Traceroute schedule (targets: %d)\n", indent, "", sched.item_size());
115     for (int i = 0; i < sched.item_size(); i++) {
116 	const SpooferTracerouteSchedule::Item &item = sched.item(i);
117         printf("#%*s  [%d] %s\n", indent, "", i, pb_ntop(family, item.dstip()));
118     }
119 }
120 
printSpoofIngressSchedule(int indent,const SpooferSpoofIngressSchedule & sched,family_t family)121 static void printSpoofIngressSchedule(int indent,
122     const SpooferSpoofIngressSchedule &sched, family_t family)
123 {
124     printf("#%*s SpoofIngress schedule (sources: %d)\n", indent, "", sched.srcip_size());
125     printf("#%*s  timeout: %u\n", indent, "", sched.timeout());
126     printf("#%*s  dstip: %s\n", indent, "", pb_ntop(family, sched.dstip()));
127     for (int i = 0; i < sched.srcip_size(); i++) {
128 	printf("#%*s  srcip[%d]: %s\n", indent, "", i, pb_ntop(family, sched.srcip(i)));
129     }
130 }
131 
printServerMsg(const SpooferServerMsg & msg,IPv ipv,time_t ts)132 void printServerMsg(const SpooferServerMsg &msg, IPv ipv, time_t ts)
133 {
134     FileGuard fileguard(stdout);
135     family_t family = ipvtofamily(ipv);
136 
137     if (ts)
138 	printf("# %s\n", gmTimeStr(&ts)());
139 
140     // NB: GUI and openwrt-files/spoofer depend on this format
141     printf("# ServerMessage (IPv%d):\n", ipv);
142     if (msg.has_txtmsg()) {
143 	printf("#  txtmsg: level %d: \"%s\"\n", msg.txtmsg().level(),
144 	    msg.txtmsg().body().c_str());
145     }
146     if (msg.has_hello()) {
147 	const SpooferServerHello &hello = msg.hello();
148 	printf("#  hello:\n");
149 	printf("#   sessionid: %u\n", hello.sessionid());
150 	printf("#   sessionkey: %s\n", hello.sessionkey().c_str());
151 	printf("#   clientip: %s\n", pb_ntop(family, hello.clientip()));
152 	for (int i = 0; i < hello.work_est_size(); i++) {
153 	    printf("#   work_est[%d]: %s, %u\n", i,
154 		names_TestType.fromId(msg.hello().work_est(i).type()),
155 		hello.work_est(i).count());
156 	}
157 	if (hello.has_upgrade()) {
158 	    printf("#   upgrade:\n");
159 	    printf("#    mandatory: %s\n", hello.upgrade().mandatory() ? "true" : "false");
160 	    printf("#    vnum: %d\n", hello.upgrade().vnum());
161 	    printf("#    vstr: %s\n", hello.upgrade().vstr().c_str());
162 	    if (hello.upgrade().has_file())
163 		printf("#    file: %s\n", hello.upgrade().file().c_str());
164 	}
165     }
166     for (int i = 0; i < msg.schedule_size(); i++) {
167 	printf("#  schedule[%d]:\n", i);
168 	// schedule holds a oneof, but we avoid oneof API for 2.4 compatibility
169 	int n_sched = 0;
170 	if (msg.schedule(i).has_spoof()) {
171 	    printSpoofSchedule(2, msg.schedule(i).spoof(), family, true);
172 	    n_sched++;
173 	}
174 	if (msg.schedule(i).has_tracefilter()) {
175 	    printTracefilterSchedule(2, msg.schedule(i).tracefilter(), family);
176 	    n_sched++;
177 	}
178 	if (msg.schedule(i).has_traceroute()) {
179 	    printTracerouteSchedule(2, msg.schedule(i).traceroute(), family);
180 	    n_sched++;
181 	}
182 	if (msg.schedule(i).has_spoofingress()) {
183 	    printSpoofIngressSchedule(2, msg.schedule(i).spoofingress(), family);
184 	    n_sched++;
185 	}
186 	if (n_sched != 1)
187 	    printf("#   WARNING: sub-schedule count %d != 1\n", n_sched);
188     }
189     if (msg.has_summary()) {
190 	printf("#  summary:\n");
191 	printf("#   AS: %u\n", msg.summary().clientasn());
192 	printf("#   privaddr: %d\n", msg.summary().privaddr());
193 	printf("#   routable: %d\n", msg.summary().routable());
194 	if (msg.summary().has_ingress_privaddr())
195 	    printf("#   ingress_privaddr: %d\n", msg.summary().ingress_privaddr());
196 	if (msg.summary().has_ingress_internal())
197 	    printf("#   ingress_internal: %d\n", msg.summary().ingress_internal());
198     }
199 }
200 
printSpoofReport(int indent,const SpooferSpoofReport & report,family_t family)201 static void printSpoofReport(int indent,
202     const SpooferSpoofReport &report, family_t family)
203 {
204     printf("#%*s Spoof report (targets: %d):\n", indent, "", report.item_size());
205     printf("#%*s  status: %s\n", indent, "", names_ReportStatus.fromId(report.status()));
206     for (int i = 0; i < report.item_size(); i++) {
207 	const SpooferSpoofReport::Item &item = report.item(i);
208 	printf("#%*s  [%d] ", indent, "", i);
209 	printf("%-*s -> ", addrStrWidth(family), pb_ntop(family, item.srcip()));
210 	if (family == AF_INET6) {
211 	    printf("%s\n#%*s    ", pb_ntop(family, item.dstip()), indent, "");
212 	} else {
213 	    printf("%-*s", addrStrWidth(family), pb_ntop(family, item.dstip()));
214 	}
215 	printf("  seq: %s  status: %s\n", item.seqno().c_str(),
216 	    names_SpoofReport_Item_Status.fromId(item.status()));
217     }
218 }
219 
printTracefilterReport(int indent,const SpooferTracefilterReport & report,family_t family ATR_UNUSED)220 static void printTracefilterReport(int indent,
221     const SpooferTracefilterReport &report, family_t family ATR_UNUSED)
222 {
223     printf("#%*s Tracefilter report:\n", indent, "");
224     printf("#%*s  status: %s\n", indent, "", names_ReportStatus.fromId(report.status()));
225 }
226 
printTracerouteReport(int indent,const SpooferTracerouteReport & report,family_t family)227 static void printTracerouteReport(int indent, const SpooferTracerouteReport &report, family_t family)
228 {
229     printf("#%*s Traceroute report (targets: %d):\n", indent, "", report.item_size());
230     printf("#%*s  status: %s\n", indent, "", names_ReportStatus.fromId(report.status()));
231     for (int i = 0; i < report.item_size(); i++) {
232 	const SpooferTracerouteReport::Item &item = report.item(i);
233 	printf("#%*s  [%d] %s\n", indent, "", i, pb_ntop(family, item.dstip()));
234 	if (item.has_text()) {
235 	    // preprend the prefix to every line of text
236 	    const char *p = item.text().c_str();
237 	    while (*p) {
238 		int len = safe_int<int>(strcspn(p, "\n"));
239 		printf("#%*s  %.*s\n", indent+2, "", len, p);
240 		if (!p[len]) break; // no newline at end
241 		p += len + 1;
242 	    }
243 	}
244     }
245 }
246 
printSpoofIngressReport(int indent,const SpooferSpoofIngressReport & report,family_t family)247 static void printSpoofIngressReport(int indent,
248     const SpooferSpoofIngressReport &report, family_t family)
249 {
250     printf("#%*s SpoofIngress report (sources: %d):\n", indent, "", report.item_size());
251     printf("#%*s  status: %s\n", indent, "", names_ReportStatus.fromId(report.status()));
252     if (report.port())
253 	printf("#%*s  port: %u\n", indent, "", report.port());
254     char prefix[80];
255     char tbuf[32];
256     snprintf(prefix, sizeof(prefix), "#%*s", indent+5, "");
257     for (int i = 0; i < report.item_size(); i++) {
258 	const SpooferSpoofIngressReport::Item &item = report.item(i);
259 	time_t ts = safe_int<time_t>(item.timestamp());
260 	struct tm *tm = gmtime(&ts);
261 	strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", tm);
262 	printf("#%*s  [%d]", indent, "", i);
263 	if (item.has_timestamp())
264 	    printf(" %s", tbuf);
265 	if (item.has_rcvd_srcip())
266 	    printf(" %s", pb_ntop(family, item.rcvd_srcip()));
267 	printf("\n");
268 	if (item.has_count())
269 	    printf("#%*s    count: %u\n", indent, "", item.count());
270 	if (item.has_payload())
271 	    printf("#%*s    payload:\n", indent, "");
272 	binDump(item.payload().data(), item.payload().size(), true, prefix);
273     }
274 }
275 
printClientMsg(const SpooferClientMsg & msg,IPv ipv,time_t ts)276 void printClientMsg(const SpooferClientMsg &msg, IPv ipv, time_t ts)
277 {
278     FileGuard fileguard(stdout);
279     family_t family = ipvtofamily(ipv);
280 
281     if (ts)
282 	printf("# %s\n", gmTimeStr(&ts)());
283 
284     // NB: openwrt-files/spoofer depends on this format
285     printf("# ClientMessage (IPv%d):\n", ipv);
286     printf("#  ready: %s\n", msg.ready() ? "true" : "false");
287     if (msg.has_hello()) {
288 	printf("#  hello:\n");
289 	printf("#   version: %u\n", msg.hello().version());
290 	printf("#   os: %s\n", msg.hello().os().c_str());
291 	printf("#   upgrade_key: %s\n", msg.hello().upgrade_key().c_str());
292 	printf("#   clientip: %s\n", pb_ntop(family, msg.hello().clientip()));
293 	printf("#   share_public: %d\n", msg.hello().share_public());
294 	printf("#   share_remedy: %d\n", msg.hello().share_remedy());
295 	printf("#   sessionkey: %s\n", msg.hello().sessionkey().c_str());
296 	printf("#   types: ");
297 	for (int i = 0; i < msg.hello().type_size(); i++) {
298 	    printf(" %s", names_TestType.fromId(msg.hello().type(i)));
299 	}
300 	printf("\n");
301     }
302     for (int i = 0; i < msg.report_size(); i++) {
303 	printf("#  report[%d]:\n", i);
304 	// report holds a oneof, but we avoid oneof API for 2.4 compatibility
305 	int n_report = 0;
306 	if (msg.report(i).has_spoof()) {
307 	    printSpoofReport(2, msg.report(i).spoof(), family);
308 	    n_report++;
309 	}
310 	if (msg.report(i).has_tracefilter()) {
311 	    printTracefilterReport(2, msg.report(i).tracefilter(), family);
312 	    n_report++;
313 	}
314 	if (msg.report(i).has_traceroute()) {
315 	    printTracerouteReport(2, msg.report(i).traceroute(), family);
316 	    n_report++;
317 	}
318 	if (msg.report(i).has_spoofingress()) {
319 	    printSpoofIngressReport(2, msg.report(i).spoofingress(), family);
320 	    n_report++;
321 	}
322 	if (n_report != 1)
323 	    printf("#   WARNING: sub-report count %d != 1\n", n_report);
324     }
325 }
326 
327