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