main.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. package main
  2. import (
  3. "bufio"
  4. "bytes"
  5. "crypto/sha1"
  6. "encoding/csv"
  7. "encoding/json"
  8. "flag"
  9. "fmt"
  10. "io"
  11. "log"
  12. "math/rand"
  13. "net"
  14. "net/http"
  15. "os"
  16. "strings"
  17. "time"
  18. "github.com/BurntSushi/toml"
  19. "github.com/google/gopacket"
  20. "github.com/google/gopacket/layers"
  21. "github.com/google/gopacket/pcap"
  22. "github.com/nats-io/nats"
  23. "github.com/nats-io/nats/encoders/protobuf"
  24. "git.scraperwall.com/scw/ajp13"
  25. "git.scraperwall.com/scw/data"
  26. "git.scraperwall.com/scw/ip"
  27. )
  28. var (
  29. doLiveCapture = flag.Bool("live", false, "Capture data in real time from a given interface")
  30. iface = flag.String("interface", "eth0", "Interface to get packets from")
  31. snapshotLen = flag.Int("snapshot-len", 8192, "Snapshot Length in Bytes")
  32. filter = flag.String("filter", "tcp", "PCAP filter expression")
  33. promiscuous = flag.Bool("promiscuous", false, "Switch interface into promiscuous mode?")
  34. natsURL = flag.String("nats-url", "nats://127.0.0.1:4222", "The URL of the NATS server")
  35. natsUser = flag.String("nats-user", "", "The user for NATS authentication")
  36. natsPassword = flag.String("nats-password", "", "The password for NATS authentication")
  37. natsQueue = flag.String("nats-queue", "requests", "The NATS queue name")
  38. natsCA = flag.String("nats-ca", "", "CA chain for NATS TLS")
  39. sleepFor = flag.Duration("sleep", 0, "Sleep this long between sending data (only when replaying a file)")
  40. requestsFile = flag.String("requests", "", "CSV file containing requests (IP and URL)")
  41. protocol = flag.String("protocol", "http", "which protocol to parse: http or ajp13")
  42. useXForwardedAsSource = flag.Bool("use-x-forwarded", false, "Use the IP address in X-Forwarded-For as source")
  43. trace = flag.Bool("trace", false, "Trace the packet capturing")
  44. apacheLog = flag.String("apache-log", "", "Parse an Apache Log file")
  45. apacheReplay = flag.String("apache-replay", "", "Apache log file to replay into the system")
  46. nginxLog = flag.String("nginx-log", "", "Nginx log file to tail")
  47. nginxFormat = flag.String("nginx-format", "", "The nginx log file format")
  48. nginxReplay = flag.String("nginx-replay", "", "Replay this nginx logfile")
  49. hostName = flag.String("hostname", "", "Override the captured hostname with this one")
  50. accessWatchKey = flag.String("access-watch-key", "", "access.watch API key")
  51. configFile = flag.String("config", "", "The location of the TOML config file")
  52. beQuiet = flag.Bool("quiet", true, "Be quiet")
  53. doVersion = flag.Bool("version", false, "Show version information")
  54. natsEC *nats.EncodedConn
  55. natsJSONEC *nats.EncodedConn
  56. natsErrorChan chan error
  57. natsIsAvailable bool
  58. count uint64
  59. timeout = -1 * time.Second
  60. ipPriv *ip.IP
  61. config Config
  62. // Version contains the program Version, e.g. 1.0.1
  63. Version string
  64. // BuildDate contains the date and time at which the program was compiled
  65. BuildDate string
  66. )
  67. // Config contains the program configuration
  68. type Config struct {
  69. Live bool
  70. Interface string
  71. SnapshotLen int
  72. Filter string
  73. Promiscuous bool
  74. NatsURL string
  75. NatsQueue string
  76. NatsUser string
  77. NatsPassword string
  78. NatsCA string
  79. SleepFor duration
  80. RequestsFile string
  81. UseXForwardedAsSource bool
  82. Quiet bool
  83. Protocol string
  84. Trace bool
  85. ApacheLog string
  86. ApacheReplay string
  87. NginxLog string
  88. NginxLogFormat string
  89. NginxReplay string
  90. HostName string
  91. AccessWatchKey string
  92. }
  93. type duration struct {
  94. time.Duration
  95. }
  96. func (d *duration) UnmarshalText(text []byte) error {
  97. var err error
  98. d.Duration, err = time.ParseDuration(string(text))
  99. return err
  100. }
  101. func (c Config) print() {
  102. fmt.Printf("Live: %t\n", c.Live)
  103. fmt.Printf("Interface: %s\n", c.Interface)
  104. fmt.Printf("SnapshotLen: %d\n", c.SnapshotLen)
  105. fmt.Printf("Filter: %s\n", c.Filter)
  106. fmt.Printf("Promiscuous: %t\n", c.Promiscuous)
  107. fmt.Printf("NatsURL: %s\n", c.NatsURL)
  108. fmt.Printf("NatsQueue: %s\n", c.NatsQueue)
  109. fmt.Printf("NatsUser: %s\n", c.NatsUser)
  110. fmt.Printf("NatsPassword: %s\n", c.NatsPassword)
  111. fmt.Printf("NatsCA: %s\n", c.NatsCA)
  112. fmt.Printf("SleepFor: %s\n", c.SleepFor.String())
  113. fmt.Printf("RequestsFile: %s\n", c.RequestsFile)
  114. fmt.Printf("Apache Log: %s\n", c.ApacheLog)
  115. fmt.Printf("Apache Replay: %s\n", c.ApacheReplay)
  116. fmt.Printf("Nginx Log: %s\n", c.NginxLog)
  117. fmt.Printf("Nginx Log Format: %s\n", c.NginxLogFormat)
  118. fmt.Printf("NginxReplay: %s\n", c.NginxReplay)
  119. fmt.Printf("HostName: %s\n", c.HostName)
  120. fmt.Printf("AccessWatchKey: %s\n", c.AccessWatchKey)
  121. fmt.Printf("UseXForwardedAsSource: %t\n", c.UseXForwardedAsSource)
  122. fmt.Printf("Protocol: %s\n", c.Protocol)
  123. fmt.Printf("Quiet: %t\n", c.Quiet)
  124. fmt.Printf("Trace: %t\n", c.Trace)
  125. }
  126. func init() {
  127. flag.Parse()
  128. nats.RegisterEncoder(protobuf.PROTOBUF_ENCODER, &protobuf.ProtobufEncoder{})
  129. }
  130. func main() {
  131. if *doVersion {
  132. version()
  133. os.Exit(0)
  134. }
  135. loadConfig()
  136. // Output how many requests per second were sent
  137. if !config.Quiet {
  138. go func(c *uint64) {
  139. for {
  140. fmt.Printf("%d requests per second\n", *c)
  141. *c = 0
  142. time.Sleep(time.Second)
  143. }
  144. }(&count)
  145. }
  146. // NATS
  147. //
  148. if config.NatsURL == "" && config.AccessWatchKey == "" {
  149. log.Fatal("No NATS URL specified (-nats-url)!")
  150. }
  151. natsIsAvailable = false
  152. natsErrorChan = make(chan error, 1)
  153. err := connectToNATS()
  154. if err != nil && config.AccessWatchKey == "" {
  155. log.Fatal(err)
  156. }
  157. go natsWatchdog(natsErrorChan)
  158. // What should I do?
  159. if config.RequestsFile != "" {
  160. replayFile()
  161. } else if config.ApacheReplay != "" {
  162. apacheLogReplay(config.ApacheReplay)
  163. } else if config.NginxReplay != "" {
  164. nginxLogReplay(config.NginxReplay, config.NginxLogFormat)
  165. } else if config.ApacheLog != "" {
  166. apacheLogCapture(config.ApacheLog)
  167. } else if config.Live {
  168. fmt.Printf("live capture (%s, %s) to %s\n", config.Interface, config.Filter, config.NatsURL)
  169. liveCapture()
  170. } else if config.NginxLog != "" && config.NginxLogFormat != "" {
  171. nginxLogCapture(config.NginxLog, config.NginxLogFormat)
  172. }
  173. }
  174. func natsWatchdog(closedChan chan error) {
  175. var lastError error
  176. for err := range closedChan {
  177. if lastError != err {
  178. lastError = err
  179. log.Println(err)
  180. }
  181. if err != nats.ErrConnectionClosed {
  182. continue
  183. }
  184. RECONNECT:
  185. for {
  186. log.Printf("Reconnecting to NATS at %s\n", *natsURL)
  187. err := connectToNATS()
  188. if err == nil {
  189. break RECONNECT
  190. }
  191. time.Sleep(1 * time.Second)
  192. }
  193. }
  194. }
  195. func connectToNATS() error {
  196. var natsConn *nats.Conn
  197. var err error
  198. if config.NatsUser != "" && config.NatsPassword != "" && config.NatsCA != "" {
  199. natsConn, err = nats.Connect(config.NatsURL, nats.UserInfo(config.NatsUser, config.NatsPassword), nats.RootCAs(config.NatsCA))
  200. } else {
  201. if config.NatsPassword != "" && config.NatsUser != "" {
  202. natsConn, err = nats.Connect(config.NatsURL, nats.UserInfo(config.NatsUser, config.NatsPassword))
  203. } else {
  204. natsConn, err = nats.Connect(config.NatsURL)
  205. }
  206. }
  207. if err != nil {
  208. return err
  209. }
  210. natsEC, err = nats.NewEncodedConn(natsConn, protobuf.PROTOBUF_ENCODER)
  211. if err != nil {
  212. return fmt.Errorf("Encoded Connection: %v", err)
  213. }
  214. natsJSONEC, err = nats.NewEncodedConn(natsConn, nats.JSON_ENCODER)
  215. if err != nil {
  216. return fmt.Errorf("Encoded Connection: %v", err)
  217. }
  218. natsIsAvailable = true
  219. return nil
  220. }
  221. func liveCapture() {
  222. ipPriv = ip.NewIP()
  223. // PCAP setup
  224. //
  225. handle, err := pcap.OpenLive(config.Interface, int32(config.SnapshotLen), config.Promiscuous, timeout)
  226. if err != nil {
  227. log.Fatal(err)
  228. }
  229. defer handle.Close()
  230. err = handle.SetBPFFilter(config.Filter)
  231. if err != nil {
  232. log.Fatal(err)
  233. }
  234. packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
  235. for packet := range packetSource.Packets() {
  236. go processPacket(packet)
  237. }
  238. }
  239. func writeLogToWatch(r *data.Request) {
  240. h := map[string]string{}
  241. if r.AcceptEncoding != "" {
  242. h["Accept-Encoding"] = r.AcceptEncoding
  243. }
  244. if r.Accept != "" {
  245. h["Accept"] = r.Accept
  246. }
  247. if r.AcceptLanguage != "" {
  248. h["Accept-Language"] = r.AcceptLanguage
  249. }
  250. if r.Cookie != "" {
  251. h["Cookie"] = r.Cookie
  252. }
  253. if r.Host != "" {
  254. h["Host"] = r.Host
  255. }
  256. if r.Referer != "" {
  257. h["Referer"] = r.Referer
  258. }
  259. if r.UserAgent != "" {
  260. h["User-Agent"] = r.UserAgent
  261. }
  262. if r.Via != "" {
  263. h["Via"] = r.Via
  264. }
  265. if r.XForwardedFor != "" {
  266. h["X-Forwarded-For"] = r.XForwardedFor
  267. }
  268. if r.XRequestedWith != "" {
  269. h["X-Requested-With"] = r.XRequestedWith
  270. }
  271. data := map[string]interface{}{
  272. "request": map[string]interface{}{
  273. "time": time.Unix(0, r.CreatedAt),
  274. "address": r.Source,
  275. "protocol": r.Protocol,
  276. "scheme": "https",
  277. "method": r.Method,
  278. "url": r.Url,
  279. "headers": h,
  280. },
  281. "response": map[string]interface{}{"status": "200"},
  282. }
  283. jdata, err := json.Marshal(data)
  284. client := &http.Client{}
  285. fmt.Println(string(jdata))
  286. buf := bytes.NewBuffer(jdata)
  287. req, err := http.NewRequest("POST", "https://log.access.watch/1.1/log", buf)
  288. req.Header.Add("Api-Key", config.AccessWatchKey)
  289. req.Header.Add("Accept", "application/json")
  290. req.Header.Add("Content-Type", "application/json")
  291. resp, err := client.Do(req)
  292. if err != nil {
  293. log.Println(err)
  294. }
  295. resp.Body.Close()
  296. }
  297. func publishRequest(queue string, request *data.Request) {
  298. if config.AccessWatchKey != "" {
  299. writeLogToWatch(request)
  300. return
  301. }
  302. if !natsIsAvailable {
  303. if rand.Intn(100) == 0 {
  304. log.Println("nats connection is not available")
  305. }
  306. return
  307. }
  308. if err := natsEC.Publish(config.NatsQueue, request); err != nil {
  309. natsErrorChan <- err
  310. if err == nats.ErrConnectionClosed {
  311. natsIsAvailable = false
  312. }
  313. }
  314. }
  315. // processPacket receives a raw packet from pcap, builds a Request item from it and sends it to the queue
  316. func processPacket(packet gopacket.Packet) {
  317. hasIPv4 := false
  318. var ipSrc, ipDst string
  319. // IPv4
  320. if ipLayer := packet.Layer(layers.LayerTypeIPv4); ipLayer != nil {
  321. ip := ipLayer.(*layers.IPv4)
  322. ipSrc = ip.SrcIP.String()
  323. ipDst = ip.DstIP.String()
  324. hasIPv4 = true
  325. }
  326. // IPv6
  327. if !hasIPv4 {
  328. if ipLayer := packet.Layer(layers.LayerTypeIPv6); ipLayer != nil {
  329. ip := ipLayer.(*layers.IPv6)
  330. ipSrc = ip.SrcIP.String()
  331. ipDst = ip.DstIP.String()
  332. }
  333. }
  334. // TCP
  335. tcpLayer := packet.Layer(layers.LayerTypeTCP)
  336. if tcpLayer == nil {
  337. return
  338. }
  339. tcp, _ := tcpLayer.(*layers.TCP)
  340. portSrc := tcp.SrcPort
  341. portDst := tcp.DstPort
  342. sequence := tcp.Seq
  343. applicationLayer := packet.ApplicationLayer()
  344. if applicationLayer == nil {
  345. return
  346. }
  347. count++
  348. if len(applicationLayer.Payload()) < 50 {
  349. log.Println("application layer too small!")
  350. return
  351. }
  352. request := data.Request{
  353. IpSrc: ipSrc,
  354. IpDst: ipDst,
  355. PortSrc: uint32(portSrc),
  356. PortDst: uint32(portDst),
  357. TcpSeq: uint32(sequence),
  358. CreatedAt: packet.Metadata().CaptureInfo.Timestamp.UnixNano(),
  359. }
  360. switch config.Protocol {
  361. case "http":
  362. err := processHTTP(&request, applicationLayer.Payload())
  363. if err != nil {
  364. log.Println(err)
  365. return
  366. }
  367. case "ajp13":
  368. err := processAJP13(&request, applicationLayer.Payload())
  369. if err != nil {
  370. log.Println(err)
  371. return
  372. }
  373. }
  374. if config.UseXForwardedAsSource && request.XForwardedFor != "" {
  375. if strings.Contains(request.XForwardedFor, ",") {
  376. ips := strings.Split(request.XForwardedFor, ",")
  377. for i := len(ips) - 1; i >= 0; i-- {
  378. ipRaw := strings.TrimSpace(ips[i])
  379. ipAddr := net.ParseIP(ipRaw)
  380. if ipAddr != nil && !ipPriv.IsPrivate(ipAddr) {
  381. request.Source = ipRaw
  382. break
  383. }
  384. }
  385. } else {
  386. ipAddr := net.ParseIP(strings.TrimSpace(request.XForwardedFor))
  387. if !ipPriv.IsPrivate(ipAddr) {
  388. request.Source = request.XForwardedFor
  389. }
  390. }
  391. }
  392. if request.Source == request.IpSrc && request.XRealIP != "" {
  393. request.Source = request.XRealIP
  394. }
  395. if config.Trace {
  396. log.Printf("[%s] %s\n", request.Source, request.Url)
  397. }
  398. publishRequest(config.NatsQueue, &request)
  399. }
  400. func processAJP13(request *data.Request, appData []byte) error {
  401. a, err := ajp13.Parse(appData)
  402. if err != nil {
  403. return fmt.Errorf("Failed to parse AJP13 request: %s", err)
  404. }
  405. request.Url = a.URI
  406. request.Method = a.Method()
  407. request.Host = a.Server
  408. request.Protocol = a.Version
  409. request.Origin = a.RemoteAddr.String()
  410. request.Source = a.RemoteAddr.String()
  411. if v, ok := a.Header("Referer"); ok {
  412. request.Referer = v
  413. }
  414. if v, ok := a.Header("Connection"); ok {
  415. request.Connection = v
  416. }
  417. if v, ok := a.Header("X-Forwarded-For"); ok {
  418. request.XForwardedFor = v
  419. }
  420. if v, ok := a.Header("X-Real-IP"); ok {
  421. request.XRealIP = v
  422. }
  423. if v, ok := a.Header("X-Requested-With"); ok {
  424. request.XRequestedWith = v
  425. }
  426. if v, ok := a.Header("Accept-Encoding"); ok {
  427. request.AcceptEncoding = v
  428. }
  429. if v, ok := a.Header("Accept-Language"); ok {
  430. request.AcceptLanguage = v
  431. }
  432. if v, ok := a.Header("User-Agent"); ok {
  433. request.UserAgent = v
  434. }
  435. if v, ok := a.Header("Accept"); ok {
  436. request.Accept = v
  437. }
  438. if v, ok := a.Header("Cookie"); ok {
  439. request.Cookie = v
  440. }
  441. if v, ok := a.Header("X-Forwarded-Host"); ok {
  442. if v != request.Host {
  443. request.Host = v
  444. }
  445. }
  446. return nil
  447. }
  448. func processHTTP(request *data.Request, appData []byte) error {
  449. reader := bufio.NewReader(strings.NewReader(string(appData)))
  450. req, err := http.ReadRequest(reader)
  451. if err != nil {
  452. return fmt.Errorf("Failed to parse HTTP header: %s", err)
  453. }
  454. request.Url = req.URL.String()
  455. request.Method = req.Method
  456. request.Referer = req.Referer()
  457. request.Host = req.Host
  458. request.Protocol = req.Proto
  459. request.Origin = request.Host
  460. if _, ok := req.Header["Connection"]; ok {
  461. request.Connection = req.Header["Connection"][0]
  462. }
  463. if _, ok := req.Header["X-Forwarded-For"]; ok {
  464. request.XForwardedFor = req.Header["X-Forwarded-For"][0]
  465. }
  466. // CloudFlare: override X-Forwarded for since it is tainted by cloudflare
  467. if _, ok := req.Header["True-Client-Ip"]; ok {
  468. request.XForwardedFor = req.Header["True-Client-Ip"][0]
  469. }
  470. if _, ok := req.Header["X-Real-Ip"]; ok {
  471. request.XRealIP = req.Header["X-Real-Ip"][0]
  472. }
  473. if _, ok := req.Header["X-Requested-With"]; ok {
  474. request.XRequestedWith = req.Header["X-Requested-With"][0]
  475. }
  476. if _, ok := req.Header["Accept-Encoding"]; ok {
  477. request.AcceptEncoding = req.Header["Accept-Encoding"][0]
  478. }
  479. if _, ok := req.Header["Accept-Language"]; ok {
  480. request.AcceptLanguage = req.Header["Accept-Language"][0]
  481. }
  482. if _, ok := req.Header["User-Agent"]; ok {
  483. request.UserAgent = req.Header["User-Agent"][0]
  484. }
  485. if _, ok := req.Header["Accept"]; ok {
  486. request.Accept = req.Header["Accept"][0]
  487. }
  488. if _, ok := req.Header["Cookie"]; ok {
  489. request.Cookie = req.Header["Cookie"][0]
  490. }
  491. request.Source = request.IpSrc
  492. return nil
  493. }
  494. // replayFile takes a file containing a list of requests (SourceIP Url) and queues the requests
  495. // e.g.
  496. // 157.55.39.229 /gross-gerau/12012260-beate-anstatt
  497. // 103.232.100.98 /weinsheim-eifel/13729444-plus-warenhandelsges-mbh
  498. func replayFile() {
  499. var req data.Request
  500. var startTs time.Time
  501. var endTs time.Time
  502. rand.Seed(time.Now().UnixNano())
  503. for {
  504. fh, err := os.Open(config.RequestsFile)
  505. if err != nil {
  506. log.Fatalf("Failed to open request file '%s': %s", config.RequestsFile, err)
  507. }
  508. c := csv.NewReader(fh)
  509. c.Comma = ' '
  510. for {
  511. if config.SleepFor.Duration > time.Nanosecond {
  512. startTs = time.Now()
  513. }
  514. r, err := c.Read()
  515. if err == io.EOF {
  516. break
  517. }
  518. if err != nil {
  519. log.Println(err)
  520. continue
  521. }
  522. req.IpSrc = r[0]
  523. req.Source = r[0]
  524. req.Url = r[1]
  525. req.UserAgent = "Munch/1.0"
  526. req.Host = "demo.scraperwall.com"
  527. req.CreatedAt = time.Now().UnixNano()
  528. publishRequest(config.NatsQueue, &req)
  529. if strings.Index(r[1], ".") < 0 {
  530. hash := sha1.New()
  531. io.WriteString(hash, r[0])
  532. fp := data.Fingerprint{
  533. ClientID: "scw",
  534. Fingerprint: fmt.Sprintf("%x", hash.Sum(nil)),
  535. Remote: r[0],
  536. Url: r[1],
  537. Source: r[0],
  538. CreatedAt: time.Now(),
  539. }
  540. if strings.HasPrefix(r[0], "50.31.") {
  541. fp.Fingerprint = "a1f2c2ee560ce6580d66d451a9c8dfbf"
  542. natsJSONEC.Publish("fingerprints_scw", fp)
  543. } else if rand.Intn(10) < 5 {
  544. natsJSONEC.Publish("fingerprints_scw", fp)
  545. }
  546. }
  547. count++
  548. if config.SleepFor.Duration >= time.Nanosecond {
  549. endTs = time.Now()
  550. if endTs.Before(startTs.Add(config.SleepFor.Duration)) {
  551. time.Sleep(config.SleepFor.Duration - endTs.Sub(startTs))
  552. }
  553. }
  554. }
  555. }
  556. }
  557. func loadConfig() {
  558. // initialize with values from the command line / environment
  559. config.Live = *doLiveCapture
  560. config.Interface = *iface
  561. config.SnapshotLen = *snapshotLen
  562. config.Filter = *filter
  563. config.Promiscuous = *promiscuous
  564. config.NatsURL = *natsURL
  565. config.NatsQueue = *natsQueue
  566. config.NatsUser = *natsUser
  567. config.NatsPassword = *natsPassword
  568. config.NatsCA = *natsCA
  569. config.SleepFor.Duration = *sleepFor
  570. config.RequestsFile = *requestsFile
  571. config.UseXForwardedAsSource = *useXForwardedAsSource
  572. config.Protocol = *protocol
  573. config.ApacheLog = *apacheLog
  574. config.ApacheReplay = *apacheReplay
  575. config.NginxLog = *nginxLog
  576. config.NginxLogFormat = *nginxFormat
  577. config.NginxReplay = *nginxReplay
  578. config.HostName = *hostName
  579. config.Quiet = *beQuiet
  580. config.Trace = *trace
  581. config.AccessWatchKey = *accessWatchKey
  582. if *configFile == "" {
  583. return
  584. }
  585. _, err := os.Stat(*configFile)
  586. if err != nil {
  587. log.Printf("%s: %s\n", *configFile, err)
  588. return
  589. }
  590. if _, err = toml.DecodeFile(*configFile, &config); err != nil {
  591. log.Printf("%s: %s\n", *configFile, err)
  592. }
  593. if !config.Quiet {
  594. config.print()
  595. }
  596. }
  597. // version outputs build information...
  598. func version() {
  599. fmt.Printf("munchclient %s, built on %s\n", Version, BuildDate)
  600. }