DEV Community

Cover image for Fake C++ Compiler with Node.js - Server - Part 2
Gurigraphics
Gurigraphics

Posted on

Fake C++ Compiler with Node.js - Server - Part 2

C++

Let's make it find the entrypoint and endpoint automatically.

1) Create data.txt with these symbols at the beginning and at the end

<?? hello world ??>
Enter fullscreen mode Exit fullscreen mode

2) Generate resources.res
3) Generate main.exe.

Server

1) Start

mkdir nodecpp
cd nodecpp
npm init -y
npm i express express-fileupload
npm i cors fs path buffer-indexof
Enter fullscreen mode Exit fullscreen mode

2) path_join.js

const path = require('path')

const path_join = function( file, folder ){
  if( folder ) return path.join(__dirname, `/${folder}/${ file }`)
  return path.join(__dirname, `/${ file }`)
}

module.exports = path_join; 
Enter fullscreen mode Exit fullscreen mode

3) server.js

var cors = require('cors')
const path = require('path')
const express = require('express')
const fileUpload = require('express-fileupload')

const app = express()
const port = 8000

var corsOptions = {
  origin: '*',
  optionsSuccessStatus: 200
}

app.use(fileUpload())
app.use(express.static(__dirname + '/www'));

var router_home = require("./routes/home")
var router_uploads = require("./routes/uploads")
var router_downloads = require("./routes/downloads")

app.use("/",          router_home)
app.use("/uploads",   router_uploads)
app.use("/downloads", router_downloads)

app.listen(port, () => {
  console.log(`Server listening on port ${port}`)
})
Enter fullscreen mode Exit fullscreen mode

Routes

4) Generate routes folder

mkdir routes
Enter fullscreen mode Exit fullscreen mode

5) routes/home.js

const express = require("express")
const router = express.Router()

const controller_home = require("../controllers/home")

router.get("/", controller_home.index);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

6) routes/uploads.js

const express = require("express")
const router = express.Router()

const controller_uploads = require("../controllers/uploads")

router.post("/", controller_uploads.uploadFile, controller_uploads.upload_txt, controller_uploads.upload_exe)

module.exports = router

Enter fullscreen mode Exit fullscreen mode

7) routes/downloads.js

const express = require("express")
const router = express.Router()

const controller_downloads = require("../controllers/downloads")

router.get("/", controller_downloads.getInfo);
router.get("/:filename", controller_downloads.getFile);

module.exports = router;

Enter fullscreen mode Exit fullscreen mode

Controllers

Generate controllers folder

mkdir controllers
Enter fullscreen mode Exit fullscreen mode

8) controllers/home.js

const path_join = require('../path_join')

exports.index = async function (req, res, next) {

  res.sendFile( path_join( "www/index.html" ) )
}

Enter fullscreen mode Exit fullscreen mode

9) controllers/uploads.js

var service_uploads = require('../services/uploads')   
var service_compiler = require('../services/compiler') 

exports.uploadFile = async function ( req, res, next ){

  if( req.files && Object.keys(req.files).length !== 0 ){

    const uploadedFile = req.files.uploadFile
    const filename = uploadedFile.name

    if( !uploadedFile.name.includes(".exe") && !uploadedFile.name.includes(".txt") ){

      return res.status(500).json({ error: 'Only EXE or TXT file are accepted' })
    }

    next()

  }else{

    res.status(400).json({ error: 'No file name' })
  } 
}

exports.upload_txt = async function ( req, res, next ){

    const uploadedFile = req.files.uploadFile
    const filename = uploadedFile.name

    if( uploadedFile.name.includes(".exe") ){

       next()

    }else{

        try{

          service_uploads.uploadFile(uploadedFile, uploadedFile.name, function( err ){

            if( err ){

              console.log(err);
              res.status(404).json({ error: 'No such file or directory' })

            }else{

               res.status(200).json({ ok: 'TXT received!' })        
            }
          })

        }catch( err ){

          console.log( err )
          res.status(500).json({ error: 'Server upload file error' })
        }
    }    
}

exports.upload_exe = async function ( req, res, next ){

    const uploadedFile = req.files.uploadFile
    const filename = uploadedFile.name
    const new_filename = "new_"+filename

    try{

      service_uploads.uploadFile(uploadedFile, uploadedFile.name, function( err ){

        if( err ){

          console.log(err);
          res.status(404).json({ error: 'No such file or directory' })

        }else{

          var result = service_compiler.compile( filename, new_filename )

          if( result == "ok" ){

           res.redirect(`downloads?filename=${new_filename}`)
           //res.redirect(`downloads/${new_filename}`)

          }else{

            res.status(404).json({ error: result })
          }          
        }
      })

    }catch( err ){

      console.log( err )
      res.status(500).json({ error: 'Server upload file error' })
    }
}

Enter fullscreen mode Exit fullscreen mode

10) controllers/downloads.js

var service_downloads = require('../services/downloads')    

exports.getInfo = async function ( req, res, next ){

  if( req.query.filename ){ 

    try{

      var download_info = service_downloads.getInfo( req )

      res.json( download_info )

    }catch( err ){

      console.log( err )
      res.status(500).json({ error: 'Server info file error' })
    }

  }else{

    res.status(400).send("No file name")
  } 
}

exports.getFile = async function ( req, res, next ){

  if( req.params.filename ){ 

    try{

      service_downloads.getFile( req.params.filename, res, function( err ){

        if( err ){

          console.log(err);
          res.status(404).json({ error: 'No such file or directory' })
        }

      })

    }catch( err ){

      console.log( err )
      res.status(500).json({ error: 'Server download file error' })
    }

  }else{

    res.status(400).json({ error: 'No file name' })
  } 
}

Enter fullscreen mode Exit fullscreen mode

Services

Generate services folder

mkdir services 
Enter fullscreen mode Exit fullscreen mode

11) services/uploads.js

const path_join = require('../path_join')

exports.uploadFile = function( uploadedFile, filename, callback ) {

  var uploadPath = path_join(filename, "uploads")

  uploadedFile.mv(uploadPath, async function (err) {

    if( err ){
      console.log(err);
      callback("Error uploading file");
    }else{
      callback(null); // No error
    }
  })
}

Enter fullscreen mode Exit fullscreen mode

12) services/downloads.js

const path_join = require('../path_join')

exports.getInfo = function( req ){

  var folder = "downloads";
  var host = req.get('host')
  var protocol = req.protocol
  var filename = req.query.filename

  var download_link = `${protocol}://${host}/${folder}/${filename}`;

  return {
    filename: filename,
    download_link: download_link
  }
}

exports.getFile = function( filename, res, callback ) {

  var file_path = path_join( filename, "uploads" )

  res.download( file_path, function( err ) {

    if( err ){
      console.log(err);
      callback("Error download file");
    }else{
      callback(null); // No error
    } 
  })
}

Enter fullscreen mode Exit fullscreen mode

13) services/compiler.js

var path_join = require('../path_join')

var slicer = require('./compiler/slicer')
const getIndex = require('./compiler/getIndex');
var merge3binary = require('./compiler/merge3binary')
var replaceBinaryText = require('./compiler/replaceBinaryText')

exports.compile = function( filename, new_filename ){

  var folder = "uploads"

  var input_path = path_join( filename, folder )
  var output_path = path_join( new_filename, folder )

  var header_path = path_join( "main_header.exe", folder )
  var middle_path = path_join( "main_middle.exe", folder )  
  var footer_path = path_join( "main_footer.exe", folder )

  var data_path = path_join( "data.txt", folder )
  var new_path = path_join( "main_middle_updated.exe", folder )

  var indexes = getIndex( input_path )

  if( indexes.error ){
    return indexes.error
  }

  var adress_start = indexes[0]
  var adress_end = indexes[1]

  // -------- Generate 3 slices
  slicer( input_path, header_path, "0x00000", adress_start)
  slicer( input_path, middle_path, adress_start, adress_end)
  slicer( input_path, footer_path, adress_end, "end")


  // -------- Update resources region
  replaceBinaryText( data_path, middle_path, new_path )


  // Merge 3 binary
  merge3binary(header_path, new_path, footer_path, output_path)

  return "ok"
}

Enter fullscreen mode Exit fullscreen mode

Compiler

Generate compiler folder inside services

mkdir compiler
Enter fullscreen mode Exit fullscreen mode

14) compiler/getIndex.js

const fs = require('fs');
const BufferIndexOf = require('buffer-indexof');

const getIndex = function( exe_path ){

  const input_data = fs.readFileSync( exe_path )

  const buffer = Buffer.from( input_data )
  const startString = Buffer.from('<??', 'utf-8')
  const endString = Buffer.from('??>', 'utf-8')

  const start = BufferIndexOf(buffer, startString)
  const endSize = BufferIndexOf(buffer, endString, start + startString.length)
  const end = endSize + endString.length

  const entryPoint = start.toString(16)
  const endPoint = end.toString(16)

  if (start === -1 || end === -1) {
    return { error: "<?? and ??> not found" }
    console.log("<?? and ??> not found")
  } else {
    console.log("Start:"+entryPoint)
    console.log("End:"+endPoint)
    return [entryPoint, endPoint]
  }
} 

module.exports = getIndex; 
Enter fullscreen mode Exit fullscreen mode

15) compiler/slicer.js

const fs = require('fs')

const slicer = function( exe_path, result_path, entry_start, entry_end ){

  var file_data;

  // Set positions
  const file_start = parseInt( entry_start, 16 );
  const file_end = parseInt( entry_end, 16 );

  // Get exe
  const input_data = fs.readFileSync( exe_path );

  // Slice
  if( entry_end == "end" ){
    file_data = input_data.slice( file_start )
  }else{
    file_data = input_data.slice( file_start, file_end )
  }  

  // Write
  fs.writeFileSync( result_path, file_data );

  console.log('Binary file sucessfull created!');
} 

module.exports = slicer; 

Enter fullscreen mode Exit fullscreen mode

16) compiler/replaceBinaryText.js

const fs = require('fs')

const replaceBinaryText = function( data_path, input_path, output_path ){

  // Get bin
  const exe = fs.readFileSync( input_path );
  const min_size = exe.length;

  // Get text
  const input_data = fs.readFileSync( data_path );
  const data_size = input_data.length;

  if( data_size > min_size ){

    console.log(`File Error. Maximum size is ${min_size}`);

  }else{

    // Fill
    const padding_buffer = Buffer.alloc(min_size - data_size, 0x00);

    // Concat
    const output_buffer = Buffer.concat([input_data, padding_buffer]);

    // Write
    fs.writeFileSync(output_path, output_buffer, 'binary');

    console.log('Binary file sucessfull created!');
  }
}

module.exports = replaceBinaryText; 

Enter fullscreen mode Exit fullscreen mode

17) compiler/merge3binary.js

const fs = require('fs')

const merge3binary = function( header, middle, footer, output_path ){

  const header_data = fs.readFileSync( header );
  const middle_data = fs.readFileSync( middle );
  const footer_data = fs.readFileSync( footer );

  const merged_buffer = Buffer.concat([header_data, middle_data, footer_data]);

  fs.writeFileSync(output_path, merged_buffer, 'binary');

  console.log('Binary file sucessfull created!');
}

module.exports = merge3binary; 

Enter fullscreen mode Exit fullscreen mode

Compiler

Generate folders: uploads and www

mkdir uploads 
mkdir www
Enter fullscreen mode Exit fullscreen mode

18) www/index.html

<!DOCTYPE html>
<html lang='en'>
  <head>
    <meta name='viewport' content='width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no'>
    <meta charset='utf-8'>
    <title>Fake Compiler C++</title>
    <link rel='stylesheet' src='style.css'>
  </head>
  <body>
    <div id='root'>

<h1>Fake Compiler C++</h1>

<form id="txt" method="post" enctype="multipart/form-data" action="/uploads">
   <input type="hidden" name="msgtype" value="2"/>
   <input type="file" name="uploadFile" accept=".txt"/>           
   <button type="submit" value="Upload" >Browse File</button>
</form>

   </div>
      <script type='javascript' src='main.js'></script>
  </body>
</html>

Enter fullscreen mode Exit fullscreen mode

19) Run

node server.js
Enter fullscreen mode Exit fullscreen mode
# Download fileInfo

http://localhost:8000/downloads?filename=new_main.exe

# Download file

http://localhost:8000/downloads/new_main.exe

# Post file

var post_txt = async() => {

  const formData = new FormData(txt);

  try {
    const response = await fetch('/uploads', {
      method: 'POST',
      body: formData
    });

    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}
Enter fullscreen mode Exit fullscreen mode

20) Demo

Fake C++ Compiler with Node.js - Frontend - Part 3

https://dev.to/gurigraphics/fake-c-compiler-with-nodejs-frontend-part-3-1m18

Top comments (0)