💾 Archived View for 80h.dev › projects › gemserv › files › src › cgi.rs.gemini captured on 2022-06-12 at 00:48:39. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2022-03-01)

-=-=-=-=-=-=-

01 #![cfg(any(feature = "cgi", feature = "scgi"))]

02 use std::collections::HashMap;

03 use std::io;

04 use std::net::SocketAddr;

05

06 #[cfg(feature = "cgi")]

07 use tokio::process::Command;

08 #[cfg(feature = "cgi")]

09 use std::path::PathBuf;

10

11 #[cfg(feature = "scgi")]

12 use std::net::ToSocketAddrs;

13 #[cfg(feature = "scgi")]

14 use tokio::io::{AsyncReadExt, AsyncWriteExt};

15 #[cfg(feature = "scgi")]

16 use tokio::net::TcpStream;

17

18 use crate::config;

19 use crate::conn;

20 use crate::logger;

21 use crate::status::Status;

22 use crate::util;

23

24 #[cfg(any(feature = "cgi", feature = "scgi"))]

25 fn envs(peer_addr: SocketAddr, x509: Option<openssl::x509::X509>, srv: &config::ServerCfg, url: &url::Url) -> HashMap<String, String> {

26 let mut envs = HashMap::new();

27 envs.insert("GATEWAY_INTERFACE".to_string(), "CGI/1.1".to_string());

28 envs.insert("GEMINI_URL".to_string(), url.to_string());

29 envs.insert("SERVER_NAME".to_string(), url.host_str().unwrap().to_string());

30 envs.insert("SERVER_PROTOCOL".to_string(), "GEMINI".to_string());

31 let addr = peer_addr.ip().to_string();

32 envs.insert("REMOTE_ADDR".to_string(), addr.clone());

33 envs.insert("REMOTE_HOST".to_string(), addr);

34 let port = peer_addr.port().to_string();

35 envs.insert("REMOTE_PORT".to_string(), port);

36 envs.insert("SERVER_SOFTWARE".to_string(), env!("CARGO_PKG_NAME").to_string());

37

38 if let Some(q) = url.query() {

39 envs.insert("QUERY_STRING".to_string(), q.to_string());

40 }

41

42 match x509 {

43 Some(x) => {

44 envs.insert("AUTH_TYPE".to_string(), "Certificate".to_string());

45

46 let cn = x.subject_name().entries_by_nid(openssl::nid::Nid::COMMONNAME);

47 for c in cn {

48 let cd = match c.data().as_utf8() {

49 Ok(n) => n.to_string(),

50 _ => "".to_string(),

51 };

52 envs.insert("REMOTE_USER".to_string(), cd);

53 }

54

55 envs.insert("TLS_CLIENT_HASH".to_string(), util::fingerhex(&x));

56 },

57 None => {},

58 }

59

60 match &srv.server.cgienv {

61 Some(c) => {

62 for (k, v) in c.iter() {

63 envs.insert(k.clone(), v.clone());

64 }

65 }

66 None => {}

67 }

68 envs

69 }

70

71 #[cfg(any(feature = "cgi", feature = "scgi"))]

72 fn check(byt: u8, peer_addr: SocketAddr, u: &url::Url) -> bool {

73 match byt {

74 49 => {

75 logger::logger(peer_addr, Status::Input, u.as_str());

76 },

77 50 => {

78 logger::logger(peer_addr, Status::Success, u.as_str());

79 },

80 51..=54 => {}

81 _ => {

82 logger::logger(peer_addr, Status::CGIError, u.as_str());

83 return false;

84 },

85 }

86 true

87 }

88

89 #[cfg(feature = "cgi")]

90 pub async fn cgi(

91 con: &mut conn::Connection,

92 srv: &config::ServerCfg,

93 path: PathBuf,

94 url: &url::Url,

95 script_name: String,

96 path_info: String

97 ) -> Result<(), io::Error> {

98

99 let x509 = con.stream.ssl().peer_certificate();

100 let mut envs = envs(con.peer_addr, x509, srv, &url);

101 envs.insert("SCRIPT_NAME".into(), script_name);

102 envs.insert("PATH_INFO".into(), path_info);

103

104 match path.parent() {

105 Some(p) => {

106 std::env::set_current_dir(p)?;

107 },

108 None => {},

109 }

110

111 let cmd = Command::new(path.to_str().unwrap())

112 .env_clear()

113 .envs(&envs)

114 .output();

115

116 let cmd = match tokio::time::timeout(tokio::time::Duration::from_secs(5), cmd).await {

117 Ok(c) => {

118 match c {

119 Ok(cc) => cc,

120

121 Err(_) => {

122 logger::logger(con.peer_addr, Status::CGIError, url.as_str());

123 con.send_status(Status::CGIError, None).await?;

124 return Ok(());

125 },

126 }

127 },

128 Err(_) => {

129 logger::logger(con.peer_addr, Status::CGIError, url.as_str());

130 con.send_status(Status::CGIError, None).await?;

131 return Ok(());

132 },

133 };

134

135 if !cmd.status.success() {

136 logger::logger(con.peer_addr, Status::CGIError, url.as_str());

137 con.send_status(Status::CGIError, None).await?;

138 return Ok(());

139 }

140 let cmd = String::from_utf8(cmd.stdout).unwrap();

141 if !check(cmd.as_bytes()[0], con.peer_addr, url) {

142 con.send_status(Status::CGIError, None).await?;

143 return Ok(());

144 }

145

146 con.send_raw(cmd.as_bytes()).await?;

147 return Ok(());

148 }

149

150 #[cfg(feature = "scgi")]

151 pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection, srv: &config::ServerCfg) -> Result<(), io::Error> {

152 let addr = addr

153 .to_socket_addrs()?

154 .next()

155 .ok_or_else(|| io::Error::from(io::ErrorKind::AddrNotAvailable))?;

156

157 let mut stream = match TcpStream::connect(&addr).await {

158 Ok(s) => s,

159 Err(_) => {

160 logger::logger(con.peer_addr, Status::CGIError, u.as_str());

161 con.send_status(Status::CGIError, None).await?;

162 return Ok(());

163 }

164 };

165 let x509 = con.stream.ssl().peer_certificate();

166 let envs = envs(con.peer_addr, x509, srv, &u);

167 let len = 0usize;

168 let mut byt = String::from(format!("CONTENT_LENGTH\x00{}\x00SCGI\x001\x00

169 RQUEST_METHOD\x00POST\x00REQUEST_URI\x00{}\x00", len, u.path()));

170 for (k, v) in envs.iter() {

171 byt.push_str(&format!("{}\x00{}\x00", k, v));

172 }

173 byt = byt.len().to_string() + ":" + &byt + ",";

174

175 stream.write_all(byt.as_bytes()).await?;

176 stream.flush().await?;

177

178 let mut buf = vec![];

179 if let Err(_) = tokio::time::timeout(

180 tokio::time::Duration::from_secs(5), stream.read_to_end(&mut buf)).await {

181 logger::logger(con.peer_addr, Status::CGIError, u.as_str());

182 con.send_status(Status::CGIError, None).await?;

183 return Ok(());

184 }

185 let req = String::from_utf8_lossy(&buf[..]);

186 if !check(req.as_bytes()[0], con.peer_addr, &u) {

187 con.send_status(Status::CGIError, None).await?;

188 return Ok(());

189 }

190

191 con.send_raw(req.as_bytes()).await?;

192 Ok(())

193 }