/* * Handle HTTPS requests. * @param resp http.ResponseWriter - response buffer * @param req *http.Request - request data */ func handler(resp http.ResponseWriter, req *http.Request) { // get requested resource reference ref := req.URL.String() mth := req.Method logger.Println(logger.DBG_ALL, "[https] "+mth+" "+ref) //----------------------------------------------------------------- // check for POST request (document upload) //----------------------------------------------------------------- if mth == "POST" { // get upload data rdr, _, err := req.FormFile("file") if err != nil { logger.Println(logger.INFO, "[https] Error accessing uploaded file: "+err.Error()) // show error page ref = "/upload_err.html" mth = "GET" } else { content := make([]byte, 0) if err = sid.ProcessStream(rdr, 4096, func(data []byte) bool { content = append(content, data...) return true }); err != nil { logger.Println(logger.INFO, "[https] Error accessing uploaded file: "+err.Error()) // show error page ref = "/upload_err.html" mth = "GET" } else { // post-process uploaded document sid.PostprocessUploadData(content) // set resource ref to response page ref = "/upload_resp.html" mth = "GET" } } } //----------------------------------------------------------------- // handle GET requests //----------------------------------------------------------------- if mth == "GET" { // set default page if ref == "/" { ref = "/index.html" } // handle resource file switch { case strings.HasSuffix(ref, ".html"): resp.Header().Set("Content-Type", "text/html") } if err := sid.ProcessFile("./www"+ref, 4096, func(data []byte) bool { // append data to response buffer resp.Write(data) return true }); err != nil { logger.Println(logger.ERROR, "[https] Resource failure: "+err.Error()) } } }
/* * Generate cover content based on the content length of a client * POST request. * @param c *sid.Cover - instance reference * @param s *sid.State - reference to cover state * @return []byte - cover content */ func FinalizeCover(c *sid.Cover, s *sid.State) []byte { id, ok := s.Data["CoverId"] if !ok || len(id) == 0 { logger.Println(logger.WARN, "[cover] No CoverId found!") return nil } out := c.Posts[id] logger.Println(logger.DBG_ALL, "[cover] *** "+string(out)) return out }
// RunService runs a TCP/UDP network service with user-defined // session handler. func RunService(network, addr string, hdlr []Service) error { // initialize control service service, err := net.Listen(network, addr) if err != nil { logger.Println(logger.ERROR, "[network] service start-up failed for '"+network+"/"+addr+"': "+err.Error()) return err } // handle connection requests go func() { for { // wait for connection request client, err := service.Accept() if err != nil { logger.Println(logger.ERROR, "[network] accept failed for '"+network+"/"+addr+"': "+err.Error()) continue } // find service interface that can handle the request accepted := false for _, srv := range hdlr { // check if connection is allowed: remote := client.RemoteAddr().String() protocol := client.RemoteAddr().Network() // check for matching protocol if !srv.CanHandle(protocol) { logger.Printf(logger.WARN, "["+srv.GetName()+"] rejected connection protocol '%s' from %s\n", protocol, remote) continue } // check for matching remote address if !srv.IsAllowed(remote) { logger.Printf(logger.WARN, "["+srv.GetName()+"] rejected connection from %s\n", remote) continue } // connection accepted logger.Printf(logger.INFO, "["+srv.GetName()+"] accepted connection from %s\n", remote) accepted = true // start handler go srv.Process(client) break } // close unhandled connections if !accepted { client.Close() } } }() // report success logger.Println(logger.INFO, "[network] service started on '"+network+"/"+addr+"'...") return nil }
// EncryptMailMessage encrypts a mail with given public key. func EncryptMailMessage(key, body []byte) ([]byte, error) { rdr := bytes.NewBuffer(key) keyring, err := openpgp.ReadArmoredKeyRing(rdr) if err != nil { logger.Println(logger.ERROR, err.Error()) return nil, err } out := new(bytes.Buffer) ct, err := armor.Encode(out, "PGP MESSAGE", nil) if err != nil { logger.Println(logger.ERROR, "Can't create armorer: "+err.Error()) return nil, err } wrt, err := openpgp.Encrypt(ct, []*openpgp.Entity{keyring[0]}, nil, &openpgp.FileHints{IsBinary: true}, nil) if err != nil { logger.Println(logger.ERROR, err.Error()) return nil, err } wrt.Write(body) wrt.Close() ct.Close() tmp := make([]byte, 30) _, err = io.ReadFull(rand.Reader, tmp) if err != nil { logger.Println(logger.ERROR, err.Error()) return nil, err } bndry := fmt.Sprintf("%x", tmp) msg := new(bytes.Buffer) msg.WriteString( "MIME-Version: 1.0\n" + "Content-Type: multipart/encrypted;\n" + " protocol=\"application/pgp-encrypted\";\n" + " boundary=\"" + bndry + "\"\n\n" + "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\n" + "--" + bndry + "\n" + "Content-Type: application/pgp-encrypted\n" + "Content-Description: PGP/MIME version identification\n\n" + "Version: 1\n\n" + "--" + bndry + "\n" + "Content-Type: application/octet-stream;\n name=\"encrypted.asc\"\n" + "Content-Description: OpenPGP encrypted message\n" + "Content-Disposition: inline;\n filename=\"encrypted.asc\"\n\n" + string(out.Bytes()) + "\n--" + bndry + "--") return msg.Bytes(), nil }
/* * Check for TCP protocol. * @param protocol string - connection protocol * @return bool - protcol handled? */ func (c *ControlSrv) CanHandle(protocol string) bool { rc := strings.HasPrefix(protocol, "tcp") if !rc { logger.Println(logger.WARN, "[sid.ctrl] Unsupported protocol '"+protocol+"'") } return rc }
/* * Check for TCP protocol. * @param protocol string - connection protocol * @return bool - protcol handled? */ func (s *HttpSrv) CanHandle(protocol string) bool { rc := strings.HasPrefix(protocol, "tcp") if !rc { logger.Println(logger.INFO, "[sid.http] Unsupported protocol '"+protocol+"'") } return rc }
// ParseSigned reads an unencrypted, but signed message. func ParseSigned(ct, addr string, getInfo MailUserInfo, body io.Reader) (*MailContent, error) { mc := new(MailContent) mc.Mode = modeSIGN boundary := extractValue(ct, "boundary") rdr := multipart.NewReader(body, boundary) for { if part, err := rdr.NextPart(); err == nil { ct = part.Header.Get("Content-Type") switch { case strings.HasPrefix(ct, "text/plain;"): data, err := ioutil.ReadAll(part) if err != nil { return nil, err } mc.Body = string(data) case strings.HasPrefix(ct, "application/pgp-signature;"): id := getIdentity(getInfo, infoSENDER, addr) if id == nil { mc.Mode = modeUSIGN continue } buf := bytes.NewBufferString(mc.Body) if _, err := openpgp.CheckArmoredDetachedSignature(openpgp.EntityList{id}, buf, part); err != nil { return nil, err } logger.Println(logger.INFO, "Signature verified OK") } } else if err == io.EOF { break } else { return nil, err } } return mc, nil }
/* * Check for local connection: Only connections from the local * TOR exit node are accepted. * @param add string - remote address * @return bool - local address? */ func (s *HttpSrv) IsAllowed(addr string) bool { idx := strings.Index(addr, ":") ip := addr[:idx] if strings.Index(CfgData.HttpAllow, ip) == -1 { logger.Println(logger.WARN, "[sid.http] Invalid remote address '"+addr+"'") return false } return true }
/* * Start-up the HTTPS server instance. */ func httpsServe() { // check for disabled HTTPS server if Cfg.HttpsPort < 0 { logger.Println(logger.INFO, "[https] HTTPS server disabled.") return } // define handlers http.HandleFunc("/", handler) // start server addr := ":" + strconv.Itoa(Cfg.HttpsPort) logger.Println(logger.INFO, "[https] Starting server on "+addr) if err := http.ListenAndServeTLS(addr, Cfg.HttpsCert, Cfg.HttpsKey, nil); err != nil { logger.Println(logger.ERROR, "[https] "+err.Error()) } }
/* * Check for local connection. * @param add string - remote address * @return bool - local address? */ func (c *ControlSrv) IsAllowed(addr string) bool { idx := strings.Index(addr, ":") ip := addr[:idx] if strings.Index(CfgData.CtrlAllow, ip) == -1 { logger.Println(logger.WARN, "[sid.ctrl] Unsupported remote address '"+addr+"'") return false } return true }
/* * Show custom configration settings. */ func ShowCustomConfig() { // list current configuration data logger.Println(logger.INFO, "[config] !==========< custom configuration >===============") logger.Println(logger.INFO, "[config] !Image library definition: "+Cfg.ImageDefs) logger.Println(logger.INFO, "[config] !Port for HTTPS sessions: "+strconv.Itoa(Cfg.HttpsPort)) logger.Println(logger.INFO, "[config] !HTTPS certificate: "+Cfg.HttpsCert) logger.Println(logger.INFO, "[config] !HTTPS SSL key: "+Cfg.HttpsKey) logger.Println(logger.INFO, "[config] !==========================================") }
/* * Translate tag source attribute: if the source specification is an * URI of the form "<scheme>://<server>/<path>/<to>/<resource...>" it * is transformed to an absolute path on on the sending server (that is * the SID instance) that can later be translated back to its original * form; it looks like "/&<scheme>/<server>/<path>/<to>/<resource...>" * @param tag *Tag - tag to be translated * @return string - translated tag */ func (c *Cover) translateTag(tag *Tag) string { if src, ok := tag.attrs["src"]; ok { // translate "src" attribute of tag trgt := translateURI(src) logger.Printf(logger.INFO, "[sid.cover] URI translation of '%s' => '%s'\n", src, trgt) tag.attrs["src"] = trgt } else if src, ok := tag.attrs["href"]; ok { // translate "href" attribute of tag trgt := translateURI(src) logger.Printf(logger.INFO, "[sid.cover] URI translation of '%s' => '%s'\n", src, trgt) tag.attrs["href"] = trgt } else { // failed to access reference attribute?! s := tag.String() logger.Println(logger.ERROR, "[sid.cover] Tag translation failed: "+s) return s } // return tag representation return tag.String() }
/* * Initialize image handler: read image definitions from the file * specified by the "defs" argument. * @param defs string - name of XML-based image definitions */ func InitImageHandler() { // prepare parsing of image references imgList = make([]*ImageRef, 0) rdr, err := os.Open(Cfg.ImageDefs) if err != nil { // terminate application in case of failure logger.Println(logger.ERROR, "[images] Can't read image definitions -- terminating!") logger.Println(logger.ERROR, "[images] "+err.Error()) os.Exit(1) } defer rdr.Close() // parse XML file and build image reference list decoder := xml.NewDecoder(rdr) var list ImageList if err = decoder.Decode(&list); err != nil { // terminate application in case of failure logger.Println(logger.ERROR, "[images] Can't decode image definitions -- terminating!") logger.Println(logger.ERROR, "[images] "+err.Error()) os.Exit(1) } for _, img := range list.Image { logger.Println(logger.DBG, "[images]: image="+img.Name) // get size of image file fi, err := os.Stat(img.Path) if err != nil { logger.Println(logger.ERROR, "[images] image '"+img.Path+"' missing!") continue } // clone to reference instance ir := &ImageRef{ name: img.Name, comment: img.Comment, path: img.Path, mime: img.Mime, size: int(fi.Size()), } // add to image list imgList = append(imgList, ir) } logger.Printf(logger.INFO, "[images] %d images available\n", len(imgList)) }
/* * Setup configuration data: Handle SID configuration data and * call custom handler for non-standard configuration data */ func InitConfig() { // process command line arguments CfgData.CfgFile = *flag.String("c", CfgData.CfgFile, "configuration file") flag.String("L", CfgData.LogFile, "logfile name") flag.Bool("l", CfgData.LogState, "file-based logging") flag.Int("p", CfgData.CtrlPort, "control session port") flag.Int("h", CfgData.HttpPort, "HTTP session port") flag.Parse() // read configuration from file logger.Println(logger.INFO, "[sid.config] using configuration file '"+CfgData.CfgFile+"'") cfg, err := os.Open(CfgData.CfgFile) if err != nil { logger.Println(logger.WARN, "[sid.config] configuration file not available -- using defaults") return } // configuration file exists: read parameters rdr := bufio.NewReader(cfg) err = parser.Parser(rdr, callback) if err != nil { logger.Printf(logger.ERROR, "[sid.config] error reading configuration file: %v\n", err) os.Exit(1) } logger.Println(logger.INFO, "[sid.config] configuration file complete.") // handle command line flags that may override options specified in the // configuration file (or are default values) flag.Visit(func(f *flag.Flag) { val := f.Value.String() logger.Printf(logger.INFO, "[sid.config] Overriding '%s' with '%s'\n", f.Usage, val) switch f.Name { case "L": CfgData.LogFile = val case "l": CfgData.LogState = (val == "true") case "p": CfgData.CtrlPort, _ = strconv.Atoi(val) case "h": CfgData.HttpPort, _ = strconv.Atoi(val) } }) // turn on logging if specified on command line or config file if CfgData.LogState { logger.Println(logger.INFO, "[sid.config] File logging requested.") if !logger.LogToFile(CfgData.LogFile) { CfgData.LogState = false } } // set networking parameter network.Delay = 1000000 // 1ms network.Retries = 1000 // max. 1s network.Timeout, _ = time.ParseDuration("100us") // 0.1ms proxy := "<None>" if CfgData.UseSocks { proxy = CfgData.SocksAddr } // list current configuration data logger.Println(logger.INFO, "[sid.config] !==========< configuration >===============") logger.Println(logger.INFO, "[sid.config] ! Configuration file: "+CfgData.CfgFile) logger.Println(logger.INFO, "[sid.config] !Port for control sessions: "+strconv.Itoa(CfgData.CtrlPort)) logger.Println(logger.INFO, "[sid.config] ! Port for HTTP sessions: "+strconv.Itoa(CfgData.HttpPort)) logger.Println(logger.INFO, "[sid.config] ! SOCKS proxy: "+proxy) logger.Println(logger.INFO, "[sid.config] !==========================================") }
/* * Handle (HTML) resource request with special cases like "upload". * @param c *sid.Cover - instance reference * @param s *sid.State - reference to cover state * @return body string - HTML page body * @return id string - identifier for cover content (or "") */ func HandleRequest(c *sid.Cover, s *sid.State) (body string, id string) { //================================================================= // Handle upload request on root page //================================================================= if s.ReqResource == "/" { // create boundary identifier and load next image delim := sid.CreateId(28) img := GetNextImage() // create uploadable content content := make([]byte, 0) if err := sid.ProcessFile(img.path, 4096, func(data []byte) bool { content = append(content, data...) return true }); err != nil { logger.Println(logger.ERROR, "[cover] Failed to open upload file: "+img.path) return "", "" } // build POST content suitable for upload to cover site // and save it in the handler structure lb := "\r\n" lb2 := lb + lb lb3 := lb2 + lb sep := "-----------------------------" + delim post := sep + lb + "Content-Disposition: form-data; name=\"imgUrl\"" + lb3 + sep + lb + "Content-Disposition: form-data; name=\"fileName[]\"" + lb3 + sep + lb + "Content-Disposition: form-data; name=\"file[]\"; filename=\"" + img.name + "\"" + lb + "Content-Type: " + img.mime + lb2 + string(content) + lb + sep + lb + "Content-Disposition: form-data; name=\"alt[]\"\n\n" + img.comment + lb + sep + lb + "Content-Disposition: form-data; name=\"new_width[]\"" + lb3 + sep + lb + "Content-Disposition: form-data; name=\"new_height[]\"" + lb3 + sep + lb + "Content-Disposition: form-data; name=\"submit\"" + lb2 + "Upload" + lb + sep + "--" + lb2 c.Posts[delim] = []byte(post) // assemble upload form action := "/" + delim + "/upload" total := len(c.Posts[delim]) + 32 return "<h1>Upload your document:</h1>\n" + "<script type=\"text/javascript\">\n" + "function a(){" + "b=document.u.file.files.item(0).getAsDataURL();" + "e=document.u.file.value.length;" + "c=Math.ceil(3*(b.substring(b.indexOf(\",\")+1).length+3)/4);" + "f=" + strconv.Itoa(total) + "-c-e-307;" + "if(f<0){alert(\"File size exceeds limit - can't upload!!\");}else{" + "d=\"\";for(i=0;i<f;i++){d+=b.charAt(i%c)}" + "document.u.rnd.value=d;" + "document.u.submit();" + "}}\n" + "document.write(\"" + "<form enctype=\\\"multipart/form-data\\\" action=\\\"" + action + "\\\" method=\\\"post\\\" name=\\\"u\\\">" + "<p><input type=\\\"file\\\" name=\\\"file\\\"/></p>" + "<p><input type=\\\"button\\\" value=\\\"Upload\\\" onclick=\\\"a()\\\"/></p>" + "<input type=\\\"hidden\\\" name=\\\"rnd\\\" value=\\\"\\\"/>" + "</form>\");\n" + "</script>" + "<noscript><hr/><p><font color=\"red\"><b>" + "Uploading files requires JavaScript enabled! Please change the settings " + "of your browser and try again...</b></font></p><hr/>" + "</noscript>\n" + "<hr/>\n", delim } //================================================================= // Successful upload //================================================================= if s.ReqResource == "/thumbnail" { logger.Println(logger.INFO, "[cover] Successful upload detected!") return "<h1>Upload was successful!</h1>", "" } //================================================================= // Error during upload //================================================================= logger.Println(logger.INFO, "[cover] Failed upload detected!") return "<h1>Upload was NOT successful -- please retry later!</h1>", "" }
/* * Transform cover server response: Substitute absolute URLs in the * response to local links to be handled by the request translations. * @param s *state - reference to state information * @param data []byte - response data from cover server * @param num int - length of response data * @return []data - transformed response (send to client) */ func (c *Cover) xformResp(s *State, data []byte, num int) []byte { // log incoming packet inStr := string(data[0:num]) logger.Printf(logger.DBG_HIGH, "[sid.cover] %d bytes received from cover server.\n", num) logger.Println(logger.DBG_ALL, "[sid.cover] Incoming response:\n"+inStr+"\n") // setup reader and response size := num rdr := bytes.NewBuffer(data[0:num]) resp := "" // initial response package if s.RespMode == 0 { // use identical line break sequence lb := "\r\n" if strings.Index(inStr, lb) == -1 { lb = "\n" } // start of new response encountered: parse header fields hdr: for { // get next line (terminated by line break) line, err := rdr.ReadString('\n') line = strings.TrimRight(line, "\n\r") if err != nil { // header is not complete: wait for next response fragment logger.Println(logger.WARN, "[sid.cover] Response header fragmented!") logger.Println(logger.DBG, "[sid.cover] Assembled response:\n"+resp) if size != len(resp) { logger.Printf(logger.WARN, "[sid.cover] DIFF(response:1) = %d\n", len(resp)-size) } return []byte(resp) } // check if header is available at all.. if strings.HasPrefix(line, "<!") { logger.Println(logger.INFO, "[sid.cover] No response header found: "+line) break hdr } // parse response header switch { //----------------------------------------------------- // Header parsing complete //----------------------------------------------------- case len(line) == 0: // we have parsed the header; continue with body logger.Println(logger.DBG_ALL, "[sid.cover] Incoming response header:\n"+resp) // drop length encoding on gzip content break hdr //----------------------------------------------------- // Status line //----------------------------------------------------- case strings.HasPrefix(line, "HTTP/"): // split line into parts parts := strings.Split(line, " ") status, _ := strconv.Atoi(parts[1]) logger.Printf(logger.DBG, "[sid.cover] response status: %d\n", status) if status != 200 { return data[:size] } //----------------------------------------------------- // Content-Type: //----------------------------------------------------- case strings.HasPrefix(line, "Content-Type: "): // split line into parts parts := strings.Split(line, " ") s.RespType = strings.TrimRight(parts[1], ";") logger.Println(logger.DBG_HIGH, "[sid.cover] response type: "+s.RespType) //----------------------------------------------------- // Content-Encoding: //----------------------------------------------------- case strings.HasPrefix(line, "Content-Encoding: "): // split line into parts parts := strings.Split(line, " ") s.RespEnc = parts[1] logger.Println(logger.DBG_HIGH, "[sid.cover] response encoding: "+s.RespEnc) //----------------------------------------------------- // location: //----------------------------------------------------- case strings.HasPrefix(line, "location: "): // split line into parts parts := strings.Split(line, " ") line = "location: " + translateURI(parts[1]) logger.Println(logger.DBG_HIGH, "[sid.cover] changing location => "+line) } // assemble response resp += line + lb } // add delimiter line resp += lb // adjust remaining content size num -= len(resp) } // start of HTML response? if s.RespMode == 0 { //------------------------------------------------------------- // start HTML response //------------------------------------------------------------- if strings.HasPrefix(s.RespType, "text/html") { // start of a new HTML response. Use pre-defined HTML page // to initialize response. var coverId string = "" s.RespPending, coverId = c.HandleRequest(c, s) s.Data["CoverId"] = coverId } // switch to next mode s.RespMode = 1 } switch { //------------------------------------------------------------- // assemble HTML response //------------------------------------------------------------- case strings.HasPrefix(s.RespType, "text/html"): // do content translation (collect resource tags) done := parseHTML(rdr, s.RespHdr, s.RespTags, s.RespXtra) // sync replacement body (cover content) if response has // been completely processed. if done { c.SyncCover(c, s) } // start of HTML? if s.RespMode == 1 { // initial HTML sequence resp += htmlIntro // output header if available if s.RespHdr.Count() > 0 { hdr := c.assembleHeader(s.RespHdr, num) resp += hdr num -= len(hdr) } // handle HTML body s.RespMode = 2 // open body tag str := "<body>\n" resp += str num -= len(str) } // continue to assemble HTML body body := c.assembleBody(s, num, done) resp += body num -= len(body) if done { // close body and HTML resp += htmlOutro num -= len(htmlOutro) } // we are done with this response packer, but have still response // data to transfer. Fill up with padding sequence. resp += padding(num) // return response data if size != len(resp) { logger.Printf(logger.WARN, "[sid.cover] DIFF(response:2) = %d\n", len(resp)-size) } logger.Println(logger.DBG_ALL, "[sid.cover] Translated response:\n"+resp) return []byte(resp) //------------------------------------------------------------- // Images: Images are considered harmless, so we simply // pass them back to the client. //------------------------------------------------------------- case strings.HasPrefix(s.RespType, "image/"): logger.Println(logger.DBG, "[sid.cover] Image data passed to client") return data[0:size] //------------------------------------------------------------- // JavaScript: Simply replace any JavaScript content with // spaces (looks like the client browser has disabled // JavaScript). //------------------------------------------------------------- case strings.HasPrefix(s.RespType, "application/x-javascript"): // padding to requested size for n := 0; n < num; n++ { resp += " " } // return response data logger.Println(logger.DBG, "[sid.cover] JavaScript scrubbed") if size != len(resp) { logger.Printf(logger.WARN, "[sid.cover] DIFF(response:3) = %d\n", len(resp)-size) } return []byte(resp) //------------------------------------------------------------- // CSS: Simply replace any style sheets with spaces. No image // references in CSS are parsed (looks like those are cached // resources to an eavesdropper) //------------------------------------------------------------- case strings.HasPrefix(s.RespType, "text/css"): // padding to requested size for n := 0; n < num; n++ { resp += " " } // return response data logger.Println(logger.DBG, "[sid.cover] CSS scrubbed") if size != len(resp) { logger.Printf(logger.WARN, "[sid.cover] DIFF(response:4) = %d\n", len(resp)-size) } return []byte(resp) } //return untranslated response logger.Println(logger.ERROR, "[sid.cover] Unhandled response!") return data[0:size] }
/* * Transform client request: this is supposed to work on fragmented * requests if necessary (currently not really supported) * @param s *state - reference to state information * @param data []byte - request data from client * @param num int - length of request in bytes * @return []byte - transformed request (send to cover server) */ func (c *Cover) xformReq(s *State, data []byte, num int) []byte { inStr := string(data[0:num]) logger.Printf(logger.DBG_HIGH, "[sid.cover] %d bytes received from client.\n", num) logger.Println(logger.DBG_ALL, "[sid.cover] Incoming request:\n"+inStr+"\n") // assemble transformed request rdr := bufio.NewReader(strings.NewReader(inStr)) req := "" hasContentEncoding := false // expected content encoding defined? //hasTransferEncoding := false // expected transfer encoding defined? mime := "text/html" // expected content type targetHost := c.Name // request resource from this host (default) balance := 0 // balance between incoming and outgoing information // use identical line break sequence lb := "\r\n" if strings.Index(inStr, lb) == -1 { lb = "\n" } for s.ReqState == RS_HDR { // get next line (terminated by line break) b, broken, _ := rdr.ReadLine() if b == nil || len(b) == 0 { if !broken { s.ReqState = RS_HDR_COMPLETE } break } line := strings.TrimRight(string(b), "\r\n") // transform request data switch { //--------------------------------------------------------- // POST command: upload document // This command triggers the upload of a document to SID // that is covered by an upload to the cover site of the // same length. //--------------------------------------------------------- case strings.HasPrefix(line, "POST "): // split line into parts parts := strings.Split(line, " ") logger.Printf(logger.DBG_HIGH, "[sid.cover] POST '%s'\n", parts[1]) // POST uri encodes the key to the cover POST content and the // target POST URL elem := strings.Split(parts[1], "/") s.ReqBoundaryOut = elem[1] uri := "" for i := 2; i < len(elem); i++ { uri += "/" + elem[i] } // try to get pre-defined cover content. if no cover content // has been constructed yet, the 'reqCoverPost' will contain // nil and the content is constructed later when the content // length of the incoming request is known. s.ReqCoverPost = c.GetPostContent(s.ReqBoundaryOut) s.ReqCoverPostPos = 0 // if URI refers to an external host, split into // host reference and resource specification if pos := strings.Index(uri, "://"); pos != -1 { rem := string(uri[pos+3:]) pos = strings.Index(rem, "/") if pos != -1 { targetHost = rem[0:pos] uri = rem[pos:] logger.Printf(logger.INFO, "[sid.cover] URI split: '%s', '%s'\n", targetHost, uri) } else { logger.Printf(logger.WARN, "[sid.cover] URI split failed on '%s'\n", uri) } } else { targetHost = c.Name } // assemble new POST request s.ReqResource = uri req += "POST " + uri + " HTTP/1.0" + lb s.ReqMode = REQ_POST // keep balance balance += (len(parts[1]) - len(uri)) //--------------------------------------------------------- // GET command: request resource // If the requested resource identifier is a translated // entry, we need to translate that back into its original // form. Translated entries start with "/&". // It is assumed, that a "GET" line is one of the first // lines in a request and therefore never fragmented. // N.B.: We also force HTTP/1.0 to ensure that no // chunking is used by the server (easier parsing). //--------------------------------------------------------- case strings.HasPrefix(line, "GET "): // split line into parts parts := strings.Split(line, " ") logger.Printf(logger.DBG_HIGH, "[sid.cover] resource='%s'\n", parts[1]) // perform translation (if required) uri := translateURI(parts[1]) logger.Printf(logger.INFO, "[sid.cover] URI translation: '%s' => '%s'\n", parts[1], uri) // if URI refers to an external host, split into // host reference and resource specification if pos := strings.Index(uri, "://"); pos != -1 { rem := string(uri[pos+3:]) pos = strings.Index(rem, "/") if pos != -1 { targetHost = rem[0:pos] uri = rem[pos:] logger.Printf(logger.INFO, "[sid.cover] URI split: '%s', '%s'\n", targetHost, uri) } else { logger.Printf(logger.WARN, "[sid.cover] URI split failed on '%s'\n", uri) } } else { targetHost = c.Name } // assemble new resource request s.ReqResource = uri req += "GET " + uri + " HTTP/1.0" + lb s.ReqMode = REQ_GET // keep balance balance += (len(parts[1]) - len(uri)) //--------------------------------------------------------- // Host reference: change to hostname of cover server // This translation may leed to unbalanced request sizes; // the balance will be equalled in a later line // It is assumed, that a "Host:" line is one of the first // lines in a request and therefore never fragmented. //--------------------------------------------------------- case strings.HasPrefix(line, "Host: "): // split line into parts parts := strings.Split(line, " ") // replace hostname reference logger.Printf(logger.DBG_HIGH, "[sid.cover] Host replaced with '%s'\n", targetHost) req += "Host: " + targetHost + lb // keep track of balance balance += (len(parts[1]) - len(targetHost)) //--------------------------------------------------------- // try to get balance straight on language header line: // "Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" //--------------------------------------------------------- //case s.ReqBalance != 0 && strings.HasPrefix (line, "Accept-Language: "): // @@@TODO: Is this the right place to balance the translation? //--------------------------------------------------------- // Acceptable content encoding: we only want plain HTML //--------------------------------------------------------- case strings.HasPrefix(line, "Accept-Encoding: "): // split line into parts parts := strings.Split(line, " ") hasContentEncoding = true if mime == "text/html" && parts[1] != "identity" { // change to identity encoding for HTML pages repl := "Accept-Encoding: identity" balance += len(repl) - len(line) req += repl + lb } else { req += line + lb } /* //--------------------------------------------------------- // Acceptable transfer encoding: we only want no chunking //--------------------------------------------------------- case strings.HasPrefix (line, "Transfer-Encoding: "): // split line into parts parts := strings.Split (line, " ") hasTransferEncoding = true if mime == "text/html" && parts[1] != "identity" { // change to identity transfer for HTML pages repl := "Transfer-Encoding: identity" balance += len(repl) - len(line) req += repl + lb } else { req += line + lb } */ //--------------------------------------------------------- // Expected content type //--------------------------------------------------------- case strings.HasPrefix(line, "Content-Type: "): // split line into parts parts := strings.Split(line, " ") mime = parts[1] // remember boundary definition if s.ReqMode == REQ_POST { // strip "boundary=" s.ReqBoundaryIn = string(parts[2][9:]) logger.Println(logger.DBG_HIGH, "[sid.cover] Boundary="+s.ReqBoundaryIn) repl := parts[0] + " " + mime + " boundary=---------------------------" + s.ReqBoundaryOut balance += len(repl) - len(line) req += repl + lb } else { req += line + lb } //--------------------------------------------------------- // Referer //--------------------------------------------------------- case strings.HasPrefix(line, "Referer: "): repl := "Referer: " + c.Protocol + "://" + targetHost + "/" balance += len(repl) - len(line) req += repl + lb //--------------------------------------------------------- // Connection //--------------------------------------------------------- case strings.HasPrefix(line, "Connection: "): // split line into parts parts := strings.Split(line, " ") if parts[1] != "close" { repl := "Connection: close" balance += len(repl) - len(line) req += repl + lb } else { req += line + lb } //--------------------------------------------------------- // Keep-Alive: //--------------------------------------------------------- case strings.HasPrefix(line, "Keep-Alive: "): // don't add spec balance -= len(line) //--------------------------------------------------------- // Content-Length //--------------------------------------------------------- case strings.HasPrefix(line, "Content-Length: "): // do we have a pre-defined cover content? if s.ReqCoverPost == nil || s.ReqCoverPost[0] == '!' { // split line into parts parts := strings.Split(line, " ") // get incoming content length s.ReqContentLength, _ = strconv.Atoi(parts[1]) // construct/expand cover content for given size s.ReqCoverPost = c.FinalizeCover(c, s) } // use cover content to construct a content length repl := "Content-Length: " + strconv.Itoa(len(s.ReqCoverPost)) balance += len(repl) - len(line) req += repl + lb //--------------------------------------------------------- // add unchanged request lines. //--------------------------------------------------------- default: req += line if !broken { req += lb } } } // check for completed header in this pass if s.ReqState == RS_HDR_COMPLETE { // add delimiting empty line req += lb // post-process header if mime == "text/html" { if !hasContentEncoding { // enforce identity encoding for HTML pages repl := "Accept-Encoding: identity" balance += len(repl) req += repl + lb } /* if !hasTransferEncoding { // enforce identity transfer for HTML pages repl := "Transfer-Encoding: identity" balance += len(repl) req += repl + lb } */ } if s.ReqMode == REQ_POST { // switch state s.ReqState = RS_CONTENT } else { // we are done s.ReqState = RS_DONE } } // handle processing of request contents for POST requests if s.ReqState == RS_CONTENT { // parse data until end of request for { // get next line (terminated by line break) // and adjust number of bytes read b, _, err := rdr.ReadLine() if err != nil { break } line := strings.TrimRight(string(b), "\r\n") //logger.Println (logger.DBG_ALL, "[sid.cover] POST content: " + line + "\n") if !s.ReqUpload { // check for start of document if strings.Index(line, "name=\"file\";") != -1 { s.ReqUpload = true s.ReqUploadData = "" } } else { if strings.Index(line, s.ReqBoundaryIn) != -1 { s.ReqUpload = false s.ReqUploadOK = PostprocessUploadData([]byte(s.ReqUploadData)) } // we are uploading client data s.ReqUploadData += line + lb } } // build new request data binReq := []byte(req) copy(data, binReq) pos := len(binReq) count := num - pos // we have "count" bytes of response data to sent out start := s.ReqCoverPostPos total := len(s.ReqCoverPost) if start < total { end := start + count s.ReqCoverPostPos = end if end > total { end = total } copy(data[pos:], s.ReqCoverPost[start:end]) pos += (end - start) } // fill up with line breaks if pos < num { fill := "" for count = num - pos; count > 0; count-- { fill += "\n" } data = append(data[0:pos], []byte(fill)...) pos = num } outStr := string(data[0:pos]) logger.Printf(logger.DBG_HIGH, "[sid.cover] %d bytes send to cover server.\n", pos) logger.Println(logger.DBG_ALL, "[sid.cover] Outgoing request:\n"+outStr+"\n") return data[0:pos] } // check for completed request processing if s.ReqState == RS_DONE { if balance != 0 { logger.Printf(logger.WARN, "[sid.cover] Unbalanced request: %d bytes diff\n", balance) } } else { // padding of request with line breaks (if assembled request is smaller; GET only) for num > len(req) && s.ReqMode == REQ_GET { req += "\n" } // return transformed request if num != len(req) { logger.Printf(logger.WARN, "[sid.cover] DIFF(request) = %d\n", len(req)-num) } logger.Printf(logger.DBG_ALL, "[sid.cover] Transformed request:\n"+req+"\n") } return []byte(req) }
/* * Connect to cover server * @return net.Conn - connection to cover server (or nil) */ func (c *Cover) connect() net.Conn { // establish connection directly or via proxy. var ( conn net.Conn err error ) if CfgData.UseSocks { conn, err = network.Socks5Connect("tcp", c.Name, c.Port, CfgData.SocksAddr) if err != nil { // can't connect logger.Printf(logger.ERROR, "[sid.cover] failed to connect to cover server through SOCKS5 proxy: %s\n", err.Error()) return nil } logger.Println(logger.INFO, "[sid.cover] connected to cover server through SOCKS5 proxy...") } else { addr := c.Name + ":" + strconv.Itoa(c.Port) conn, err = net.Dial("tcp", addr) if err != nil { // can't connect logger.Printf(logger.ERROR, "[sid.cover] failed to connect to cover server: %s\n", err.Error()) return nil } logger.Println(logger.INFO, "[sid.cover] directly connected to cover server...") } // allocate state information and add to state list // initialize struct with default data c.States[conn] = &State{ //------------------------------------------------------------- // Request state //------------------------------------------------------------- ReqMode: REQ_UNKNOWN, ReqState: RS_HDR, ReqResource: "", ReqBoundaryIn: "", ReqBoundaryOut: "", ReqCoverPost: nil, ReqCoverPostPos: 0, ReqUpload: false, ReqUploadOK: false, ReqUploadData: "", //------------------------------------------------------------- // Response state //------------------------------------------------------------- RespPending: "", RespEnc: "", RespMode: 0, RespSize: 0, RespType: "text/html", RespHdr: NewTagList(), RespTags: NewTagList(), RespXtra: NewTagList(), //------------------------------------------------------------- // Additional data //------------------------------------------------------------- Data: make(map[string]string), } return conn }
func Startup() { logger.Println(logger.INFO, "[sid] ==============================") logger.Println(logger.INFO, "[sid] SID v0.3 -- Server In Disguise") logger.Println(logger.INFO, "[sid] (c) 2011-2012 Bernd R. Fix >Y<") logger.Println(logger.INFO, "[sid] ==============================") //----------------------------------------------------------------- // Handle SID configuration: read configuration data from config // file 'sid.cfg' in current directory (can be overridden by the // '-c <file>' option on the command line. If no configuration file // exists, default values for all config options are used. // Configuration options used on the command line will override // options defined in the config file (or default options). //----------------------------------------------------------------- // handle configuration file and command line options // (turns on file-based logging if specified on command line) InitConfig() //----------------------------------------------------------------- // Initialize cover-related settings //----------------------------------------------------------------- InitDocumentHandler(CfgData.Upload) if CustomInitialization == nil { logger.Println(logger.ERROR, "[sid] No custom initialization function defined -- aborting!") return } cover := CustomInitialization() //----------------------------------------------------------------- // Start network services //----------------------------------------------------------------- // create control service. ch := make(chan bool) ctrl := &ControlSrv{ch} ctrlList := []network.Service{ctrl} // create HTTP service httpList := make([]network.Service, 0) if HttpActive { http := NewHttpSrv(cover) httpList = append(httpList, http) } if HttpFallback != nil { httpList = append(httpList, HttpFallback) } // start network services network.RunService("tcp", ":"+strconv.Itoa(CfgData.CtrlPort), ctrlList) if len(httpList) > 0 { network.RunService("tcp", ":"+strconv.Itoa(CfgData.HttpPort), httpList) } // wait for termination <-ch logger.Println(logger.INFO, "[sid] Application terminated.") }
/* * Handle HTML-like content: since we can't be sure that the body is error- * free* HTML, we need a lazy parser for the fields we are interested in. * The parser builds a list of inline resources referenced in the HTML file; * these are the resources the client browser will request when loading the * HTML page, so that it behaves like a genuine access if monitored by an * eavesdropper. This function adds to an existing list of resources (from * a previous cover server response). * @param rdr *io.Reader - buffered reader for parsing * @param links *TagList - (current) list of inline resources (header) * @param list *TagList - (current) list of inline resources (body) * @param xtra *TagList - (current) list of inline resources (special interest tags) * @return bool - end of parsing (HTML closed)? */ func parseHTML(rdr io.Reader, links *TagList, list *TagList, xtra *TagList) bool { // try to use GO html tokenizer to parse the content tk := html.NewTokenizer(rdr) stack := make([]*Tag, 0) var tag *Tag closed := false loop: for { // get next HTML tag toktype := tk.Next() if toktype == html.ErrorToken { // parsing error: most probable case is that the tag spans // fragments. This is only a problem if it concerns a tag // for possible translation (currently unhandled) switch tk.Err() { case io.ErrUnexpectedEOF: logger.Println(logger.ERROR, "[sid.html] Error parsing content: "+tk.Err().Error()) return false case io.EOF: break loop } } if toktype == html.StartTagToken { // we are starting a tag. push to stack until EndTagToken is encountered. tag = readTag(tk) if tag != nil { logger.Println(logger.DBG_ALL, "[sid.html] tag pushed to stack: "+tag.String()) stack = append(stack, tag) } continue loop } else if toktype == html.EndTagToken { n, _ := tk.TagName() name := string(n) pos := len(stack) - 1 if pos >= 0 && stack[pos].name == name { // found matching tag tag = stack[pos] stack = stack[0:pos] logger.Println(logger.DBG_ALL, "[sid.html] tag popped from stack: "+tag.String()) } else { if name == "html" { logger.Println(logger.DBG_ALL, "body ==> </html>") closed = true } continue loop } } else if toktype == html.SelfClosingTagToken { tag = readTag(tk) if tag == nil { continue loop } //logger.Println(logger.DBG_ALL, "[sid.html] direct tag : "+tag.String()) } else { //logger.Println(logger.DBG_ALL, "[sid.html] ? "+toktype.String()) continue loop } // post-process tag and add to appropriate tag list switch { case tag.name == "img": // add/replace dimensions tag.attrs["width"] = "1" tag.attrs["height"] = "1" // add to list list.Put(tag) logger.Println(logger.DBG, "[sid.html] body => "+tag.String()) case tag.name == "script": list.Put(tag) logger.Println(logger.DBG, "[sid.html] body => "+tag.String()) case tag.name == "link": links.Put(tag) logger.Println(logger.DBG, "[sid.html] hdr => "+tag.String()) case tag.name == "input" && tag.attrs["type"] == "hidden": xtra.Put(tag) logger.Println(logger.DBG, "[sid.html] xtra => "+tag.String()) // default: // logger.Println (logger.DBG_ALL, "*** " + tag.String()) } } return closed }
/* * Handle client connection. * @param client net.Conn - connection to client */ func (c *ControlSrv) Process(client net.Conn) { b := bufio.NewReadWriter(bufio.NewReader(client), bufio.NewWriter(client)) for repeat := true; repeat; { // show control menu b.WriteString("\n-----------------------------------\n") b.WriteString("Change (L)og level [" + logger.GetLogLevel() + "]\n") b.WriteString("(T)erminate application\n") b.WriteString("e(X)it\n") b.WriteString("-----------------------------------\n") b.WriteString("Enter command: ") b.Flush() // get command input cmd, err := readCmd(b) if err != nil { break } // handle command logger.Println(logger.INFO, "[sid.ctrl] command '"+cmd+"'") switch cmd { //------------------------------------------------- // Terminate application //------------------------------------------------- case "T": b.WriteString("Are you sure? Enter YES to continue: ") b.Flush() cmd, _ = readCmd(b) if cmd == "YES" { logger.Println(logger.WARN, "[sid.ctrl] Terminating application") b.WriteString("Terminating application...") b.Flush() c.Ch <- true } else { logger.Println(logger.WARN, "[sid.ctrl] Response '"+cmd+"' -- Termination aborted!") b.WriteString("Wrong response -- Termination aborted!") b.Flush() } //------------------------------------------------- // Change logging level //------------------------------------------------- case "L": b.WriteString("Enter new log level (ERROR,WARN,INFO,DBG_HIGH,DBG,DBG_ALL): ") b.Flush() cmd, _ = readCmd(b) logger.SetLogLevelFromName(cmd) //------------------------------------------------- // Quit control session //------------------------------------------------- case "X": repeat = false //------------------------------------------------- // Unknown command //------------------------------------------------- default: b.WriteString("Unkonwn command '" + cmd + "'\n") } } client.Close() }
/* * Handle client connection. * @param client net.Conn - connection to client */ func (s *HttpSrv) Process(client net.Conn) { // close client connection on function exit defer client.Close() // allocate buffer data := make([]byte, 32768) // open a new connection to the cover server cover := s.hndlr.connect() if cover == nil { // failed to open connection to cover server return } // close connection to cover server on exit defer s.hndlr.disconnect(cover) // get associated state info state := s.hndlr.GetState(cover) // handle session loop for { //------------------------------------------------------------- // Upstream message passing //------------------------------------------------------------- // get data from cover server. n, ok := network.RecvData(cover, data, "http") if !ok { // epic fail: terminate session return } // send pending response to client if n > 0 { // transform response resp := s.hndlr.xformResp(state, data, n) // send incoming response data to client if !network.SendData(client, resp, "http") { // terminate session on failure logger.Println(logger.ERROR, "[sid.http] Failed to send data to client.") return } } //------------------------------------------------------------- // Downstream message passing //------------------------------------------------------------- // get data from client. n, ok = network.RecvData(client, data, "http") if !ok { // epic fail: terminate session return } // send pending client request if n > 0 { // transform request req := s.hndlr.xformReq(state, data, n) // send request to cover server if !network.SendData(cover, req, "http") { // terminate session on failure logger.Println(logger.ERROR, "[sid.http] Failed to send data to cover.") return } } } }
// ParseEncrypted parses a encrypted (and possibly signed) message. func ParseEncrypted(ct, addr string, getInfo MailUserInfo, body io.Reader) (*MailContent, error) { mc := new(MailContent) mc.Mode = modeENC boundary := extractValue(ct, "boundary") rdr := multipart.NewReader(body, boundary) for { if part, err := rdr.NextPart(); err == nil { ct = part.Header.Get("Content-Type") switch { case strings.HasPrefix(ct, "application/pgp-encrypted"): buf, err := ioutil.ReadAll(part) if err != nil { return nil, err } logger.Printf(logger.DBG, "application/pgp-encrypted: '%s'\n", strings.TrimSpace(string(buf))) continue case strings.HasPrefix(ct, "application/octet-stream;"): rdr, err := armor.Decode(part) if err != nil { return nil, err } pw := "" pwTmp := getInfo(infoPASSPHRASE, "") switch pwTmp.(type) { case string: pw = pwTmp.(string) } prompt := func(keys []openpgp.Key, symmetric bool) ([]byte, error) { priv := keys[0].PrivateKey if priv.Encrypted { priv.Decrypt([]byte(pw)) } buf := new(bytes.Buffer) priv.Serialize(buf) return buf.Bytes(), nil } id := getIdentity(getInfo, infoIDENTITY, "") md, err := openpgp.ReadMessage(rdr.Body, openpgp.EntityList{id}, prompt, nil) if err != nil { return nil, err } if md.IsSigned { mc.Mode = modeSIGNENC id := getIdentity(getInfo, infoSENDER, addr) if id == nil { mc.Mode = modeUSIGNENC content, err := ioutil.ReadAll(md.UnverifiedBody) if err != nil { return nil, err } mc.Body = string(content) continue } md.SignedBy = crypto.GetKeyFromIdentity(id, crypto.KeySign) md.SignedByKeyId = md.SignedBy.PublicKey.KeyId mc.Key, err = crypto.GetArmoredPublicKey(id) if err != nil { return nil, err } content, err := ioutil.ReadAll(md.UnverifiedBody) if err != nil { return nil, err } if md.SignatureError != nil { return nil, md.SignatureError } logger.Println(logger.INFO, "Signature verified OK") m, err := mail.ReadMessage(bytes.NewBuffer(content)) if err != nil { return nil, err } ct = m.Header.Get("Content-Type") mc2, err := ParsePlain(ct, m.Body) if err != nil { return nil, err } mc.Body = mc2.Body } default: return nil, errors.New("Unhandled MIME part: " + ct) } } else if err == io.EOF { break } else { return nil, err } } return mc, nil }
/* * Client upload data received. * @param data []byte - uploaded document data * @return bool - post-processing successful? */ func PostprocessUploadData(data []byte) bool { logger.Println(logger.INFO, "[sid.upload] Client upload received") logger.Println(logger.DBG_ALL, "[sid.upload] Client upload data:\n"+string(data)) baseName := uploadPath + "/" + CreateId(16) // check if we use a shared secret scheme if reviewer == nil { // no: store content unencrypted. fname := baseName + ".document" wrt, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) if err != nil { logger.Printf(logger.ERROR, "[sid.upload] Can't create document file '%s'\n", fname) return false } // write content and close file wrt.Write(data) wrt.Close() } else { // yes: use shared secret scheme to store upload in encrypted form. var ( err error engine cipher.Block = nil wrt io.WriteCloser = nil ct io.WriteCloser = nil pt io.WriteCloser = nil ) //----------------------------------------------------------------- // setup AES-256 for encryption //----------------------------------------------------------------- key := crypto.RandBytes(32) if engine, err = aes.NewCipher(key); err != nil { // should not happen at all; epic fail if it does logger.Println(logger.ERROR, "[sid.upload] Failed to setup AES cipher!") return false } bs := engine.BlockSize() iv := crypto.RandBytes(bs) enc := cipher.NewCFBEncrypter(engine, iv) logger.Println(logger.DBG_ALL, "[sid.upload] key:\n"+hex.Dump(key)) logger.Println(logger.DBG_ALL, "[sid.upload] IV:\n"+hex.Dump(iv)) //----------------------------------------------------------------- // encrypt client document into file //----------------------------------------------------------------- // open file for output fname := baseName + ".document.aes256" if wrt, err = os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666); err != nil { logger.Printf(logger.ERROR, "[sid.upload] Can't create document file '%s'\n", fname) return false } // write iv first wrt.Write(iv) // encrypt binary data for the document logger.Println(logger.DBG_ALL, "[sid.upload] AES256 in:\n"+hex.Dump(data)) enc.XORKeyStream(data, data) logger.Println(logger.DBG_ALL, "[sid.upload] AES256 out:\n"+hex.Dump(data)) // write to file wrt.Write(data) wrt.Close() //----------------------------------------------------------------- // create shares from secret //----------------------------------------------------------------- secret := new(big.Int).SetBytes(key) n := len(reviewer) shares := crypto.Split(secret, prime, n, treshold) recipient := make([]*openpgp.Entity, 1) for i, ent := range reviewer { // generate filename based on key id id := strconv.FormatUint(ent.PrimaryKey.KeyId&0xFFFFFFFF, 16) fname = baseName + "." + strings.ToUpper(id) + ".gpg" // create file for output if wrt, err = os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666); err != nil { logger.Printf(logger.ERROR, "[sid.upload] Can't create share file '%s'\n", fname) continue } // create PGP armorer if ct, err = armor.Encode(wrt, "PGP MESSAGE", nil); err != nil { logger.Printf(logger.ERROR, "[sid.upload] Can't create armorer: %s\n", err.Error()) wrt.Close() continue } // encrypt share to file recipient[0] = ent if pt, err = openpgp.Encrypt(ct, recipient, nil, nil, nil); err != nil { logger.Printf(logger.ERROR, "[sid.upload] Can't create encrypter: %s\n", err.Error()) ct.Close() wrt.Close() continue } pt.Write([]byte(shares[i].P.String() + "\n")) pt.Write([]byte(shares[i].X.String() + "\n")) pt.Write([]byte(shares[i].Y.String() + "\n")) pt.Close() ct.Close() wrt.Close() } } // report success return true }
// Socks5ConnectTimeout connects to a SOCKS5 proxy with timeout. func Socks5ConnectTimeout(proto string, addr string, port int, proxy string, timeout time.Duration) (net.Conn, error) { var ( conn net.Conn err error ) if proto != "tcp" { logger.Printf(logger.ERROR, "[network] Unsupported protocol '%s'.\n", proto) return nil, errors.New("Unsupported protocol (TCP only)") } p, err := url.Parse(proxy) if err != nil { return nil, err } if len(p.Scheme) > 0 && p.Scheme != "socks5" { logger.Printf(logger.ERROR, "[network] Invalid proxy scheme '%s'.\n", p.Scheme) return nil, errors.New("Invalid proxy scheme") } idx := strings.Index(p.Host, ":") if idx == -1 { logger.Printf(logger.ERROR, "[network] Invalid host definition '%s'.\n", p.Host) return nil, errors.New("Invalid host definition (missing port)") } pPort, err := strconv.Atoi(p.Host[idx+1:]) if err != nil || port < 1 || port > 65535 { logger.Printf(logger.ERROR, "[network] Invalid port definition '%d'.\n", pPort) return nil, errors.New("Invalid host definition (port out of range)") } if timeout == 0 { conn, err = net.Dial("tcp", p.Host) } else { conn, err = net.DialTimeout("tcp", p.Host, timeout) } if err != nil { logger.Printf(logger.ERROR, "[network] failed to connect to proxy server: %s\n", err.Error()) return nil, err } data := make([]byte, 1024) //----------------------------------------------------------------- // negotiate authentication //----------------------------------------------------------------- data[0] = 5 // SOCKS version data[1] = 1 // One available authentication method data[2] = 0 // No authentication required if timeout > 0 { conn.SetDeadline(time.Now().Add(timeout)) } if n, err := conn.Write(data[:3]); n != 3 { logger.Printf(logger.ERROR, "[network] failed to write to proxy server: %s\n", err.Error()) conn.Close() return nil, err } if timeout > 0 { conn.SetDeadline(time.Now().Add(timeout)) } if n, err := conn.Read(data); n != 2 { logger.Printf(logger.ERROR, "[network] failed to read from proxy server: %s\n", err.Error()) conn.Close() return nil, err } if data[0] != 5 || data[1] == 0xFF { logger.Println(logger.ERROR, "[network] proxy server refuses non-authenticated connection.") conn.Close() return nil, err } //----------------------------------------------------------------- // connect to target (request/reply processing) //----------------------------------------------------------------- dn := []byte(addr) size := len(dn) data[0] = 5 // SOCKS versions data[1] = 1 // connect to target data[2] = 0 // reserved data[3] = 3 // domain name specified data[4] = byte(size) // length of domain name for i, v := range dn { data[5+i] = v } data[5+size] = (byte)(port / 256) data[6+size] = (byte)(port % 256) if timeout > 0 { conn.SetDeadline(time.Now().Add(timeout)) } if n, err := conn.Write(data[:7+size]); n != (7 + size) { logger.Printf(logger.ERROR, "[network] failed to write to proxy server: %s\n", err.Error()) conn.Close() return nil, err } if timeout > 0 { conn.SetDeadline(time.Now().Add(timeout)) } _, err = conn.Read(data) if err != nil { conn.Close() return nil, err } if data[1] != 0 { err = errors.New(socksState[data[1]]) logger.Printf(logger.ERROR, "[network] proxy server failed: %s\n", err.Error()) conn.Close() return nil, err } //return connection return conn, nil }