further fixes to CGI 1.1 implementation

Signed-off-by: kim (grufwub) <grufwub@gmail.com>
master
kim (grufwub) 4 years ago
parent 810d57ad66
commit 6ac26c2225

@ -37,9 +37,6 @@ func setupInitialCgiEnviron() []string {
envKeyValue("PATH", SafeExecPath),
envKeyValue("COLUMNS", strconv.Itoa(Config.PageWidth)),
envKeyValue("GOPHER_CHARSET", Config.CharSet),
// envKeyValue("SERVER_ARCH", runtime.GOARCH),
// envKeyValue("SERVER_DESCRIPTION", description),
// envKeyValue("SERVER_VERSION", GophorVersion),
}
}
@ -52,12 +49,20 @@ func executeCgi(request *FileSystemRequest) ([]byte, *GophorError) {
cgiEnv = append(cgiEnv, envKeyValue("SERVER_PORT", request.Host.Port)) /* MUST be set to the server port that client is connecting to */
cgiEnv = append(cgiEnv, envKeyValue("REMOTE_ADDR", request.Client.Ip)) /* Remote client addr, MUST be set */
cgiEnv = append(cgiEnv, envKeyValue("QUERY_STRING", request.Parameters[0])) /* URL encoded search or parameter string, MUST be set even if empty */
/* This way we get query string without initial delimiter */
var queryString string
if len(request.Parameters[0]) > 0 {
queryString = request.Parameters[0][1:]
} else {
queryString = request.Parameters[0]
}
cgiEnv = append(cgiEnv, envKeyValue("QUERY_STRING", queryString)) /* URL encoded search or parameter string, MUST be set even if empty */
// cgiEnv = append(cgiEnv, envKeyValue("PATH_INFO", "")) /* Sub-resource to be fetched by script, derived from path hierarch portion of URI. NOT URL encoded */
cgiEnv = append(cgiEnv, envKeyValue("PATH_INFO", "")) /* Sub-resource to be fetched by script, derived from path hierarch portion of URI. NOT URL encoded */
cgiEnv = append(cgiEnv, envKeyValue("PATH_TRANSLATED", request.AbsPath())) /* Take PATH_INFO, parse as local URI and append root dir */
// cgiEnv = append(cgiEnv, envKeyValue("SCRIPT_NAME", "")) /* URI path (not URL encoded) which could identify the CGI script (rather than script's output) */
cgiEnv = append(cgiEnv, envKeyValue("SCRIPT_NAME", "/"+request.RelPath())) /* URI path (not URL encoded) which could identify the CGI script (rather than script's output) */
/* We ignore these due to just CBA and we're not implementing authorization yet */
// cgiEnv = append(cgiEnv, envKeyValue("AUTH_TYPE", "")) /* Any method used my server to authenticate user, MUST be set if auth'd */
// cgiEnv = append(cgiEnv, envKeyValue("CONTENT_TYPE", "")) /* Only a MUST if HTTP content-type set (so never for gopher) */
// cgiEnv = append(cgiEnv, envKeyValue("REMOTE_IDENT", "")) /* Remote client identity information */
@ -66,16 +71,9 @@ func executeCgi(request *FileSystemRequest) ([]byte, *GophorError) {
/* Non-standard */
cgiEnv = append(cgiEnv, envKeyValue("SELECTOR", request.SelectorPath()))
// cgiEnv = append(cgiEnv, envKeyValue("LOCAL_ADDR", ""))
// cgiEnv = append(cgiEnv, envKeyValue("SCRIPT_FILENAME", ""))
cgiEnv = append(cgiEnv, envKeyValue("SCRIPT_FILENAME", request.AbsPath()))
cgiEnv = append(cgiEnv, envKeyValue("DOCUMENT_ROOT", request.RootDir))
// cgiEnv = append(cgiEnv, envKeyValue("GOPHER_FILETYPE", ""))
// cgiEnv = append(cgiEnv, envKeyValue("GOPHER_REFERER", ""))
// cgiEnv = append(cgiEnv, envKeyValue("TLS", "")
// cgiEnv = append(cgiEnv, envKeyValue("SERVER_TLS_PORT", ""),
// cgiEnv = append(cgiEnv, envKeyValue("SESSION_ID", ""))
// cgiEnv = append(cgiEnv, envKeyValue("SERVER_HOST", ""))
// cgiEnv = append(cgiEnv, envKeyValue("SEARCHREQUEST", ""))
cgiEnv = append(cgiEnv, envKeyValue("REQUEST_URI", "/"+request.RelPath()+request.Parameters[0]))
return execute(cgiEnv, request.AbsPath(), nil)
}
@ -133,7 +131,7 @@ func execute(env []string, path string, args []string) ([]byte, *GophorError) {
if exitCode != 0 {
/* If non-zero exit code return error */
//errContents, gophorErr := readBuffer(errBuffer)
Config.SysLog.Error("", "Error executing: %s\n", cmd.String(), args)
Config.SysLog.Error("", "Error executing: %s\n", cmd.String())
return nil, &GophorError{ CommandExitCodeErr, err }
} else {
/* If zero exit code try return outContents and no error */

@ -87,6 +87,7 @@ func (fs *FileSystem) HandleRequest(request *FileSystemRequest) ([]byte, *Gophor
/* Append footer text (contains last line) and return */
output = append(output, Config.FooterText...)
return output, nil
/* Regular file */

@ -241,8 +241,7 @@ func formatGophermapFooter(text string, useSeparator bool) []byte {
ret = append(ret, buildInfoLine(line)...)
}
}
ret = append(ret, []byte(LastLine)...)
return ret
return append(ret, []byte(LastLine)...)
}
/* Replace standard replacement strings */

@ -16,7 +16,7 @@ func parseRequestString(request string) (string, []string) {
}
/* Use strings.TrimPrefix() as it returns empty string for zero length string */
return request[:i], []string{ strings.TrimPrefix(request[i:], "?") }
return request[:i], []string{ request[i:] }
}
/* Parse line type from contents */

@ -0,0 +1,136 @@
package main
import (
"path"
"strings"
)
/* TODO: having 2 separate rootdir string values in Host and RootDir
* doesn't sit right with me. It cleans up code a lot for now
* but could get confusing. Figure out a more elegant way of
* structuring the filesystem request that gets passed around.
*/
type FileSystemRequest struct {
/* A file system request with any possible required
* data required. Either handled through FileSystem or to
* direct function like listDir()
*/
/* Virtual host and client information */
Host *ConnHost
Client *ConnClient
/* File path information */
RootDir string
Rel string
Abs string
/* Other parameters */
Parameters []string /* CGI-bin params will be 1 length slice, shell commands populate >=1 */
}
func NewSanitizedFileSystemRequest(host *ConnHost, client *ConnClient, request string) *FileSystemRequest {
/* Split dataStr into request path and parameter string (if pressent) */
requestPath, parameters := parseRequestString(request)
requestPath = sanitizeRequestPath(host.RootDir, requestPath)
return NewFileSystemRequest(host, client, host.RootDir, requestPath, parameters)
}
func NewFileSystemRequest(host *ConnHost, client *ConnClient, rootDir, requestPath string, parameters []string) *FileSystemRequest {
return &FileSystemRequest{
host,
client,
rootDir,
requestPath,
path.Join(rootDir, requestPath),
parameters,
}
}
func (r *FileSystemRequest) SelectorPath() string {
if r.Rel == "." {
return "/"
} else {
return "/"+r.Rel
}
}
func (r *FileSystemRequest) AbsPath() string {
return r.Abs
}
func (r *FileSystemRequest) RelPath() string {
return r.Rel
}
func (r *FileSystemRequest) JoinSelectorPath(extPath string) string {
if r.Rel == "." {
return path.Join("/", extPath)
} else {
return "/"+path.Join(r.Rel, extPath)
}
}
func (r *FileSystemRequest) JoinAbsPath(extPath string) string {
return path.Join(r.AbsPath(), extPath)
}
func (r *FileSystemRequest) JoinRelPath(extPath string) string {
return path.Join(r.RelPath(), extPath)
}
func (r *FileSystemRequest) HasAbsPathPrefix(prefix string) bool {
return strings.HasPrefix(r.AbsPath(), prefix)
}
func (r *FileSystemRequest) HasRelPathPrefix(prefix string) bool {
return strings.HasPrefix(r.RelPath(), prefix)
}
func (r *FileSystemRequest) HasRelPathSuffix(suffix string) bool {
return strings.HasSuffix(r.RelPath(), suffix)
}
func (r *FileSystemRequest) HasAbsPathSuffix(suffix string) bool {
return strings.HasSuffix(r.AbsPath(), suffix)
}
func (r *FileSystemRequest) TrimRelPathSuffix(suffix string) string {
return strings.TrimSuffix(strings.TrimSuffix(r.RelPath(), suffix), "/")
}
func (r *FileSystemRequest) TrimAbsPathSuffix(suffix string) string {
return strings.TrimSuffix(strings.TrimSuffix(r.AbsPath(), suffix), "/")
}
func (r *FileSystemRequest) JoinPathFromRoot(extPath string) string {
return path.Join(r.RootDir, extPath)
}
func (r *FileSystemRequest) NewStoredRequestAtRoot(relPath string, parameters []string) *FileSystemRequest {
/* DANGER THIS DOES NOT CHECK FOR BACK-DIR TRAVERSALS */
return NewFileSystemRequest(nil, nil, r.RootDir, relPath, parameters)
}
func (r *FileSystemRequest) NewStoredRequest() *FileSystemRequest {
return NewFileSystemRequest(nil, nil, r.RootDir, r.RelPath(), r.Parameters)
}
/* Sanitize a request path string */
func sanitizeRequestPath(rootDir, requestPath string) string {
/* Start with a clean :) */
requestPath = path.Clean(requestPath)
if path.IsAbs(requestPath) {
/* Is absolute. Try trimming root and leading '/' */
requestPath = strings.TrimPrefix(strings.TrimPrefix(requestPath, rootDir), "/")
} else {
/* Is relative. If back dir traversal, give them root */
if strings.HasPrefix(requestPath, "..") {
requestPath = ""
}
}
return requestPath
}

@ -133,8 +133,6 @@ func (worker *Worker) RespondGopher(data []byte) *GophorError {
response, gophorErr := Config.FileSystem.HandleRequest(request)
if gophorErr != nil {
/* Log to system and access logs, then return error */
Config.SysLog.Error("", "Error serving %s: %s\n", dataStr, gophorErr.Error())
worker.LogError("Failed to serve: %s\n", request.AbsPath())
return gophorErr
}
worker.Log("Served: %s\n", request.AbsPath())

Loading…
Cancel
Save