Example #1
0
/*
 * 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())
		}
	}
}
Example #2
0
/*
 * 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
}
Example #3
0
// 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
}
Example #4
0
File: smtp.go Project: bfix/gospel
// 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
}
Example #5
0
File: control.go Project: bfix/sid
/*
 * 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
}
Example #6
0
File: http.go Project: bfix/sid
/*
 * 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
}
Example #7
0
File: smtp.go Project: bfix/gospel
// 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
}
Example #8
0
File: http.go Project: bfix/sid
/*
 * 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
}
Example #9
0
/*
 * 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())
	}
}
Example #10
0
File: control.go Project: bfix/sid
/*
 * 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
}
Example #11
0
/*
 * 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] !==========================================")
}
Example #12
0
File: cover.go Project: bfix/sid
/*
 * 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()
}
Example #13
0
/*
 * 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))
}
Example #14
0
File: config.go Project: bfix/sid
/*
 * 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] !==========================================")
}
Example #15
0
/*
 * 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>", ""
}
Example #16
0
File: cover.go Project: bfix/sid
/*
 * 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]
}
Example #17
0
File: cover.go Project: bfix/sid
/*
 * 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)
}
Example #18
0
File: cover.go Project: bfix/sid
/*
 * 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
}
Example #19
0
File: sid.go Project: bfix/sid
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.")
}
Example #20
0
File: html.go Project: bfix/sid
/*
 * 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
}
Example #21
0
File: control.go Project: bfix/sid
/*
 * 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()
}
Example #22
0
File: http.go Project: bfix/sid
/*
 * 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
			}
		}
	}
}
Example #23
0
File: smtp.go Project: bfix/gospel
// 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
}
Example #24
0
File: upload.go Project: bfix/sid
/*
 * 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
}
Example #25
0
File: socks.go Project: bfix/gospel
// 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
}