1// Copyright (C) 2022 Huawei Device Co., Ltd. 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14package main 15 16//遇到报错请在当前目录下执行这个命令: go mod download golang.org/x/text 17import ( 18 "bufio" 19 "bytes" 20 "crypto" 21 "crypto/rand" 22 "crypto/rsa" 23 "crypto/sha512" 24 "crypto/tls" 25 "crypto/x509" 26 "crypto/x509/pkix" 27 "encoding/base64" 28 "encoding/json" 29 "encoding/pem" 30 "flag" 31 "fmt" 32 "io" 33 "io/fs" 34 "log" 35 "math/big" 36 "mime" 37 "net" 38 "net/http" 39 "net/http/cookiejar" 40 "os" 41 "os/exec" 42 "path" 43 "path/filepath" 44 "regexp" 45 "runtime" 46 "strconv" 47 "strings" 48 "sync" 49 "time" 50) 51 52const HttpPort = 9000 53 54var exPath string 55var serveInfo string 56var msgPublishData MsgPublishData 57var hdcPublicKey string 58var hdcPrivateKey *rsa.PrivateKey 59 60// CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go 61// CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go 62func cors(fs http.Handler, version string) http.HandlerFunc { 63 return func(w http.ResponseWriter, r *http.Request) { 64 // return if you do not want the FileServer handle a specific request 65 r.Header.Add("Cross-Origin-Opener-Policy", "same-origin") 66 r.Header.Add("Cross-Origin-Embedder-Policy", "require-corp") 67 w.Header().Add("Cross-Origin-Opener-Policy", "same-origin") 68 w.Header().Add("Cross-Origin-Embedder-Policy", "require-corp") 69 w.Header().Set("Access-Control-Allow-Origin", "*") 70 w.Header().Set("Access-Control-Allow-Credentials", "true") 71 w.Header().Set("Access-Control-Allow-Headers", "x-requested-with, authorization, blade-auth") //* 72 w.Header().Set("Access-Control-Allow-Methods", "*") //* 73 w.Header().Set("Access-Control-Max-Age", "3600") 74 w.Header().Set("data-version", version) 75 w.Header().Set("Cache-Control", "no-cache") 76 w.Header().Set("Pragma", "no-cache") 77 fs.ServeHTTP(w, r) 78 } 79} 80 81func exist(path string) bool { 82 _, err := os.Stat(path) 83 if err != nil { 84 if os.IsExist(err) { 85 return true 86 } 87 return false 88 } 89 return true 90} 91 92func genSSL() { 93 if exist("cert/keyFile.key") || exist("cert/certFile.pem") { 94 fmt.Println("keyFile.key exists") 95 return 96 } 97 max := new(big.Int).Lsh(big.NewInt(1), 128) 98 serialNumber, _ := rand.Int(rand.Reader, max) 99 subject := pkix.Name{ 100 Organization: []string{"www.smartperf.com"}, 101 OrganizationalUnit: []string{"ITs"}, 102 CommonName: "www.smartperf.com", 103 } 104 certificate509 := x509.Certificate{ 105 SerialNumber: serialNumber, 106 Subject: subject, 107 NotBefore: time.Now(), 108 NotAfter: time.Now().AddDate(10, 0, 0), 109 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 110 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 111 IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, 112 } 113 chekDir("cert") 114 pk, _ := rsa.GenerateKey(rand.Reader, 1024) 115 derBytes, _ := x509.CreateCertificate(rand.Reader, &certificate509, &certificate509, &pk.PublicKey, pk) 116 certOut, _ := os.Create("cert/certFile.pem") 117 pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) 118 certOut.Close() 119 keyOut, _ := os.Create("cert/keyFile.key") 120 pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)}) 121 keyOut.Close() 122} 123 124func genRsa() { 125 // generate hdc private key 126 privateKey, err := rsa.GenerateKey(rand.Reader, 3072) 127 if err != nil { 128 fmt.Println("Generate hdc rsa private key failed") 129 return 130 } 131 hdcPrivateKey = privateKey 132 133 // generate hdc public key 134 publicKey := &privateKey.PublicKey 135 pkixPublicKey, err := x509.MarshalPKIXPublicKey(publicKey) 136 if err != nil { 137 fmt.Println(err) 138 return 139 } 140 publicKeyBlock := &pem.Block{ 141 Type: "PUBLIC KEY", 142 143 Bytes: pkixPublicKey, 144 } 145 hdcPublicKey = string(pem.EncodeToMemory(publicKeyBlock)) 146} 147 148func main() { 149 port := HttpPort 150 isOpen := 1 151 flag.IntVar(&port, "p", HttpPort, "The port number used") 152 flag.IntVar(&isOpen, "o", 1, "Whether to immediately open the website in your browser; 1 is true; 0 is false") 153 flag.Parse() 154 if isOpen < 0 || isOpen > 1 { 155 fmt.Println("Error: -o must be 0 or 1") 156 return 157 } 158 checkPort(port) 159 genSSL() 160 genRsa() 161 exPath = getCurrentAbPath() 162 fmt.Println(exPath) 163 go func() { 164 version := "" 165 readVersion, versionErr := os.ReadFile(exPath + "/version.txt") 166 if versionErr != nil { 167 version = "" 168 } else { 169 version = string(readVersion) 170 } 171 readReqServerConfig() 172 mux := http.NewServeMux() 173 mime.TypeByExtension(".js") 174 mime.AddExtensionType(".js", "application/javascript") 175 log.Println(mime.TypeByExtension(".js")) 176 mux.HandleFunc("/logger", consoleHandler) 177 mux.Handle("/application/upload/", http.StripPrefix("/application/upload/", http.FileServer(http.Dir(filepath.FromSlash(exPath+"/upload"))))) 178 mux.HandleFunc("/application/download-file", downloadHandler) 179 mux.HandleFunc("/application/serverInfo", serverInfo) 180 mux.HandleFunc("/application/hdcPublicKey", getHdcPublicKey) 181 mux.HandleFunc("/application/encryptHdcMsg", encryptHdcMsg) 182 mux.HandleFunc("/application/signatureHdcMsg", signatureHdcMsg) 183 mux.HandleFunc("/application/messagePublish", getMsgPublish) 184 fs := http.FileServer(http.Dir(exPath + "/")) 185 mux.Handle("/application/", http.StripPrefix("/application/", cors(fs, version))) 186 go func() { 187 ser := &http.Server{ 188 Addr: fmt.Sprintf(":%d", port), 189 Handler: mux, 190 } 191 log.Println(fmt.Sprintf("HTTPS[%d]服务启动", port)) 192 err := ser.ListenAndServeTLS("cert/certFile.pem", "cert/keyFile.key") 193 CheckErr(err) 194 }() 195 go func() { 196 ser := &http.Server{ 197 Addr: fmt.Sprintf(":%d", port+1), 198 Handler: mux, 199 } 200 log.Println(fmt.Sprintf("HTTP[%d]服务启动", port)) 201 err := ser.ListenAndServe() 202 CheckErr(err) 203 }() 204 if isOpen == 1 { 205 open(fmt.Sprintf("https://localhost:%d/application", port)) 206 } 207 }() 208 select {} 209} 210 211func getPidByPort(portNumber int) int { 212 resPid := -1 213 var out bytes.Buffer 214 cmdRes := exec.Command("cmd", "/c", fmt.Sprintf("netstat -ano -p tcp | findstr %d", portNumber)) 215 cmdRes.Stdout = &out 216 cmdRes.Run() 217 cmdResStr := out.String() 218 findStr := regexp.MustCompile(`\s\d+\s`).FindAllString(cmdResStr, -1) 219 if len(findStr) > 0 { 220 pid, err := strconv.Atoi(strings.TrimSpace(findStr[0])) 221 if err != nil { 222 resPid = -1 223 } else { 224 resPid = pid 225 } 226 } 227 return resPid 228} 229 230type LoggerReq struct { 231 FileName string `json:"fileName"` 232 FileSize string `json:"fileSize"` 233} 234 235func consoleHandler(w http.ResponseWriter, r *http.Request) { 236 chekDir(exPath + "/logger") 237 var now = time.Now() 238 var fileName = fmt.Sprintf("%d-%d-%d", now.Year(), now.Month(), now.Day()) 239 dst, err := os.OpenFile(exPath+"/logger/"+fileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND|os.O_SYNC, 0666) 240 CheckErr(err) 241 contentType := r.Header["Content-Type"] 242 if len(contentType) > 0 { 243 contentTypeName := contentType[0] 244 if strings.HasPrefix(contentTypeName, "application/json") { 245 decoder := json.NewDecoder(r.Body) 246 var req LoggerReq 247 decoder.Decode(&req) 248 dst.WriteString(fmt.Sprintf("%s %s (%s M)\n", now.Format("2006-01-02 15:04:05"), req.FileName, req.FileSize)) 249 fmt.Fprintf(w, fmt.Sprintf("日志写入成功%s", exPath)) 250 } 251 } 252} 253 254func serverInfo(w http.ResponseWriter, r *http.Request) { 255 w.Header().Set("Access-Control-Allow-Origin", "*") 256 w.Header().Set("request_info", serveInfo) 257 w.WriteHeader(200) 258} 259 260func getHdcPublicKey(w http.ResponseWriter, r *http.Request) { 261 w.Header().Set("Access-Control-Allow-Origin", "*") 262 w.Header().Set("Content-Type", "text/json") 263 resp(&w)(true, 0, "success", map[string]interface{}{ 264 "publicKey": hdcPublicKey, 265 }) 266} 267 268func encryptHdcMsg(w http.ResponseWriter, r *http.Request) { 269 w.Header().Set("Access-Control-Allow-Origin", "*") 270 w.Header().Set("Content-Type", "text/json") 271 hdcMsg := r.URL.Query().Get("message") 272 if len(hdcMsg) == 0 { 273 resp(&w)(false, -1, "Invalid message", nil) 274 return 275 } 276 signatures, err := rsa.SignPKCS1v15(nil, hdcPrivateKey, crypto.Hash(0), []byte(hdcMsg)) 277 if err != nil { 278 resp(&w)(false, -1, "sign failed", nil) 279 } else { 280 resp(&w)(true, 0, "success", map[string]interface{}{ 281 "signatures": base64.StdEncoding.EncodeToString(signatures), 282 }) 283 } 284} 285 286func signatureHdcMsg(w http.ResponseWriter, r *http.Request) { 287 w.Header().Set("Access-Control-Allow-Origin", "*") 288 w.Header().Set("Content-Type", "text/json") 289 hdcMsg := r.URL.Query().Get("message") 290 if len(hdcMsg) == 0 { 291 resp(&w)(false, -1, "Invalid message", nil) 292 return 293 } 294 hashed := sha512.Sum512([]byte(hdcMsg)) 295 signatures, err := rsa.SignPKCS1v15(nil, hdcPrivateKey, crypto.SHA512, hashed[:]) 296 if err != nil { 297 resp(&w)(false, -1, "sign failed", nil) 298 } else { 299 resp(&w)(true, 0, "success", map[string]interface{}{ 300 "signatures": base64.StdEncoding.EncodeToString(signatures), 301 }) 302 } 303} 304 305func parseMsgPublishFile() { 306 defer func() { 307 if r := recover(); r != nil { 308 fmt.Printf("parseMsgPublishFile happen panic, content is %+v\n", r) 309 } 310 }() 311 msgPublishData.Mux.Lock() 312 defer msgPublishData.Mux.Unlock() 313 exist, err := PathExists(msgPublishData.FilePath) 314 if err != nil || !exist { 315 return 316 } 317 buf, err := os.ReadFile(msgPublishData.FilePath) 318 if err != nil { 319 fmt.Println("read fail", err) 320 return 321 } 322 msgPublishData.Msg = string(buf) 323} 324 325func getMsgPublish(w http.ResponseWriter, r *http.Request) { 326 w.Header().Set("Access-Control-Allow-Origin", "*") 327 w.Header().Set("Content-Type", "text/json") 328 msgPublishData.Mux.RLock() 329 data := msgPublishData.Msg 330 msgPublishData.Mux.RUnlock() 331 if len(data) == 0 { 332 resp(&w)(false, -1, "msg failed", nil) 333 } else { 334 resp(&w)(true, 0, "success", map[string]interface{}{ 335 "data": data, 336 }) 337 } 338} 339 340type ServerConfig struct { 341 ServeInfo string `json:"ServeInfo"` 342 MsgPublishFile string `json:"MsgPublishFile"` 343} 344 345type MsgPublishData struct { 346 FilePath string 347 Msg string 348 Mux sync.RWMutex 349} 350 351func loopUpdateMsgPublishData() { 352 loopTime := 5 * time.Minute 353 timer := time.NewTimer(5 * time.Second) 354 for { 355 select { 356 case <-timer.C: 357 parseMsgPublishFile() 358 } 359 timer.Reset(loopTime) 360 } 361} 362 363func readReqServerConfig() { 364 serverConfigBuffer, err := os.ReadFile(exPath + "/server-config.json") 365 if err != nil { 366 return 367 } 368 var sc ServerConfig 369 err = json.Unmarshal(serverConfigBuffer, &sc) 370 if err != nil { 371 return 372 } 373 serveInfo = sc.ServeInfo 374 msgPublishData.Mux.Lock() 375 msgPublishData.FilePath = sc.MsgPublishFile 376 msgPublishData.Mux.Unlock() 377 go loopUpdateMsgPublishData() 378} 379 380func mapToJson(m map[string]interface{}) (string, error) { 381 marshal, err := json.Marshal(m) 382 if err != nil { 383 return "", err 384 } 385 var str = string(marshal) 386 return str, nil 387} 388func jsonToMap(str string) (map[string]interface{}, error) { 389 var m = make(map[string]interface{}) 390 err := json.Unmarshal([]byte(str), &m) 391 if err != nil { 392 return nil, err 393 } 394 return m, nil 395} 396 397// MkDir 创建目录 398func MkDir(path string) { 399 dir := path[0:strings.LastIndex(path, string(os.PathSeparator))] //从文件路径获取目录 400 if _, err := os.Stat(dir); err != nil { //如果目录不存在,创建目录 401 os.MkdirAll(dir, os.ModePerm) 402 } 403} 404 405func resp(w *http.ResponseWriter) func(bool, int, string, map[string]interface{}) { 406 return func(success bool, code int, msg string, obj map[string]interface{}) { 407 toJson, err := mapToJson(map[string]interface{}{ 408 "success": success, 409 "code": code, 410 "msg": msg, 411 "data": obj, 412 }) 413 if err != nil { 414 errRes, _ := mapToJson(map[string]interface{}{ 415 "success": false, 416 "code": -1, 417 "msg": err.Error(), 418 }) 419 fmt.Fprintf(*w, errRes) 420 } else { 421 fmt.Fprintf(*w, toJson) 422 } 423 } 424} 425 426func get(url string) (*http.Response, error) { 427 jar, _ := cookiejar.New(nil) 428 c := &http.Client{ 429 Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}, 430 CheckRedirect: nil, 431 Jar: jar, 432 Timeout: time.Duration(3600) * time.Second, 433 } 434 return c.Get(url) 435} 436 437func clearOverdueFile() { 438 MkDir(filepath.FromSlash(fmt.Sprintf("./upload/"))) 439 now := time.Now() 440 loc, err := time.LoadLocation("Asia/Shanghai") 441 if err != nil { 442 return 443 } 444 var checkDue = func(fileName string) bool { 445 f := getSuffixByUrl(fileName) 446 parseTime, err := time.ParseInLocation("20060102150405000", f.fileName, loc) 447 if err != nil { 448 return false 449 } 450 sub := now.Sub(parseTime) 451 if sub.Minutes() > 60 { //bigger than 60 min flag due 452 return true 453 } 454 return false 455 } 456 slash := filepath.FromSlash(fmt.Sprintf("./upload/")) 457 filepath.WalkDir(slash, func(path string, d fs.DirEntry, err error) error { 458 if checkDue(d.Name()) { 459 fmt.Println(now, "delete->", path, d.Name(), err) 460 os.Remove(path) 461 } 462 return nil 463 }) 464} 465func getSuffixByUrl(u string) struct { 466 fileName string 467 suffix string 468} { 469 lastIndex := strings.LastIndex(u, "/") 470 var f string 471 if lastIndex != -1 { 472 f = u[lastIndex:] 473 } else { 474 f = u 475 } 476 index := strings.LastIndex(f, ".") 477 if index != -1 { 478 return struct { 479 fileName string 480 suffix string 481 }{ 482 f[0:index], 483 f[index:], 484 } 485 } else { 486 return struct { 487 fileName string 488 suffix string 489 }{ 490 f, 491 "", 492 } 493 } 494} 495 496func downloadHandler(w http.ResponseWriter, r *http.Request) { 497 w.Header().Set("content-type", "text/json") 498 clearOverdueFile() 499 contentType := r.Header["Content-Type"] 500 if len(contentType) > 0 { 501 contentTypeName := contentType[0] 502 if strings.HasPrefix(contentTypeName, "application/x-www-form-urlencoded") { 503 url := r.PostFormValue("url") 504 res, err := get(url) 505 if err != nil { 506 resp(&w)(false, -1, err.Error(), nil) 507 return 508 } 509 pth := filepath.FromSlash(fmt.Sprintf("/upload/%s%s", time.Now().Format("20060102150405000"), getSuffixByUrl(url).suffix)) 510 MkDir("." + pth) 511 create, err := os.Create("." + pth) 512 if err != nil { 513 resp(&w)(false, -1, err.Error(), nil) 514 return 515 } 516 written, err := io.Copy(create, res.Body) 517 if err != nil { 518 resp(&w)(false, -1, err.Error(), nil) 519 return 520 } 521 fmt.Println(url, written) 522 pth = "/application" + pth 523 resp(&w)(true, 0, "success", map[string]interface{}{ 524 "url": pth, 525 "size": written, 526 }) 527 return 528 } 529 } 530 resp(&w)(false, -1, "请求方式错误", nil) 531} 532 533func SplitLines(s string) []string { 534 var lines []string 535 sc := bufio.NewScanner(strings.NewReader(s)) 536 for sc.Scan() { 537 lines = append(lines, sc.Text()) 538 } 539 return lines 540} 541 542func readFileFirstLine(path string) string { 543 file, err := os.Open(path) 544 if err != nil { 545 return "" 546 } 547 defer file.Close() 548 549 readFile := bufio.NewReader(file) 550 line, readErr := readFile.ReadString('\n') 551 if readErr != nil || io.EOF == err { 552 return "" 553 } 554 return line 555} 556 557func PathExists(path string) (bool, error) { 558 _, err := os.Stat(path) 559 if err == nil { 560 return true, nil 561 } 562 if os.IsNotExist(err) { 563 return false, nil 564 } 565 return false, err 566} 567 568func chekDir(path string) { 569 _, err := os.Stat(path) 570 if err != nil { 571 err := os.Mkdir(path, os.ModePerm) 572 if err != nil { 573 fmt.Printf("mkdir failed![%v]\n", err) 574 } else { 575 fmt.Printf("mkdir success!\n") 576 } 577 } 578} 579func CheckErr(err error) { 580 if err != nil { 581 log.Panicln(err) 582 } 583} 584 585func open(url string) error { 586 if isWindows() { 587 return openUrlWindows(url) 588 } else if isDarwin() { 589 return openUrlDarwin(url) 590 } else { 591 return openUrlOther(url) 592 } 593} 594 595func openUrlWindows(url string) error { 596 cmd := "cmd" 597 args := []string{"/c", "start", url} 598 return exec.Command(cmd, args...).Start() 599} 600func openUrlDarwin(url string) error { 601 var cmd = "open" 602 var args = []string{url} 603 return exec.Command(cmd, args...).Start() 604} 605func openUrlOther(url string) error { 606 var cmd = "xdg-open" 607 var args = []string{url} 608 return exec.Command(cmd, args...).Start() 609} 610 611func isWindows() bool { 612 return runtime.GOOS == "windows" 613} 614func isDarwin() bool { 615 return runtime.GOOS == "darwin" 616} 617 618func getCurrentAbPath() string { 619 dir := getExecutePath() 620 tmpDir, _ := filepath.EvalSymlinks(os.TempDir()) 621 if strings.Contains(dir, tmpDir) { 622 return getCallerPath() 623 } 624 return dir 625} 626 627func getCallerPath() string { 628 var pth string 629 _, fName, _, ok := runtime.Caller(0) 630 if ok { 631 pth = path.Dir(fName) 632 } 633 return pth 634} 635func getExecutePath() string { 636 pth, err := os.Executable() 637 if err != nil { 638 log.Fatal(err) 639 } 640 res, _ := filepath.EvalSymlinks(filepath.Dir(pth)) 641 return res 642} 643 644func checkPort(port int) { 645 if isWindows() { 646 pid := getPidByPort(port) 647 if pid != -1 { 648 res := exec.Command("cmd", "/c", fmt.Sprintf("taskkill /F /PID %d /T", pid)) 649 res.Run() 650 } 651 } 652} 653